草庐IT

C语言指针进阶(中)

长月. 2023-10-28 原文

提示: 上集内容小复习🥰🥰

int my_strlen(const char* str) {
	return 1;
}
int main() {
	//指针数组
	char* arr[10];
	//数组指针
	int arr2[5] = { 0 };
	int(*p)[5] = &arr2;  //p是一个指向数组的指针变量
	//函数指针
	int (*pf)(const char*)=&my_strlen;  //pf是一个指向函数的函数指针变量
	//int (*pf)(const char*) = my_strlen;
	//调用函数
	(*pf)("abcdef");  //函数指针调用
	my_strlen("abcdef");  //函数名调用
	pf("abcdef");  //函数指针不加*调用

	//函数指针数组:存放函数指针的数组--->在函数指针变量后加[]
	int (* pfArr[10])(const char*) = &my_strlen;

}

文章目录


前言

七、指向函数指针数组的指针

含义:🐇
指向函数指针数组的指针是一个指针
指针指向一个数组 ,数组的元素都是函数指针

区分函数指针,函数指针数组,指向函数指针数组的指针

int Add(int x, int y) {
	return x + y;
}
int Sub(int x, int y) {
	return x - y;
}
int main() {
	int (*pf)(int, int) = Add;  //函数指针变量

	int (*pfArr[4])(int, int) = { Add ,Sub};  //函数指针数组

	//pfArr是数组名,&pfArr是取出数组的地址,数组的地址应该放到数组指针里
	int (*(*ppfArr)[4])(int,int)= &pfArr; //ppfArr是一个指向函数指针数组的指针变量,把指针和指向的四个元素(*ppfArr[4])去掉,剩下的是它的类型
	return 0;
}

图解:

八、回调函数

含义:🐇
回调函数就是一个通过函数指针调用的函数,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数,回调函数不是该函数的实现方式直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应.
通俗来讲:🐻‍❄️把一个函数指针(用于接收A函数的地址值)作为形式参数传递给B函数,在B函数内部通过函数指针来调用A函数.A就被称为回调函数.
图解

1.举例说明:

在上篇博客中最后的计算机题目中,我们发现有很多代码其实是冗余的,如何简化呢?

(1)简化思路图解:

(2)代码:

void menu() {
	printf("***************************************\n");
	printf("******1.add 2.sub**********************\n");
	printf("******3.mul 4.div**********************\n");
	printf("******0.exit     **********************\n");
	printf("***************************************\n");

}
int Add(int x, int y) {
	return x + y;
}
int Sub(int x, int y) {
	return x - y;
}
int Mul(int x, int y) {
	return x * y;
}
int Div(int x, int y) {
	return x / y;
}

void Calc(int (*pf)(int, int)) {  //函数指针接收函数的地址
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}
int main() {
	int input = 0;
	
	do {
		menu();
		printf("请选择->");
		scanf("%d", &input);


		//只有输入1234的时候才需要打印结果
		//为了避免无关紧要的一些结果的输出
		switch (input) {
		case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

	return 0;
}

(3)代码逻辑图:


在这道题目中,没有直接调用加减乘除的函数,而是在特定的事件下去调用函数,用来响应这一事件.

(4)对比上篇博客中主函数的简化代码与本篇中的主函数简化代码有什么区别?

解决的问题不一样,没有哪个比哪个好的说法
上篇:当在计算机中不断增加运算的功能的时候,觉得swith…case语句中的case太多,用函数指针数组更好一些

本篇:解决的问题是如果使用原来的代码有些代码是重复冗余的,原来的代码如下图所见:

本篇是为了解决代码的冗余,使用回调函数,当特定事件结果为不同值时调用函数,形参传入不同的函数地址.

2.演示qsort函数

qsort是一个库函数,用来排序的库函数
底层用的是快速排序的方法,不是冒泡排序
q:quick quicksort

为什么是快速排序的方法不是冒泡排序的方法呢?
接下来我们来体验一下冒泡排序有哪里不好的地方🥱🥱

(1)复习冒泡排序

冒泡排序的思想分析:🐇🐇
两两相邻的元素比较.

void bubble_sort(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz-1; i++) {  //循环sz-1趟冒泡排序
		//一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz-1-i; j++) {   //每趟冒泡排序:第一趟九对进行比较,第二趟八对进行比较,以此类推,这里的j应该是<sz-1-i
			if (arr[j] > arr[j + 1]) {
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}

	}
}
void print_arr(int arr[],int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
}
int main() {
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	//排序
	//使用冒泡排序的方法
	//封装一个冒泡排序的方法(需要把数组和数组元素的个数传过去)
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	//打印
	print_arr(arr,sz);
	return 0;

}

冒泡排序有哪里不好呢?🐇
:只能排固定类型的数据,不是想排什么类型的数据就排序什么类型的数据
因为函数void bubble_sort(int arr[], int sz)的形式参数固定写死的,未来想要排序一些浮点型数据,结构体类型的数据等等是不行的

(2)qsort函数的好处

qsort函数的好处:🤞
1.现成的
2.可以排序任意类型的数据

I.认识一下qsort函数的形式参数


void qsort(void* base,  //指向了待排序数组的第一个元素
	size_t num,         //待排序的元素个数
	size_t size,		//每个元素的大小,单位是字节
	int (*compar)(const void*, const void*)   //指向一个函数,这个函数可以比较两个元素的大小.(这个形式参数是函数指针)
	//const void*, const void*是待比较的两个元素的地址
);

II.qsort可以排序任意类型的数据

1.比较2个整数的大小 > < ==
2.比较2个字符串,strcmp
3.比较2个结构体数据(学生,张三,李四) 指定比较的标准,拿什么比较

假如用冒泡排序,来排序整型数据,字符型数据,结构体数据
需要变的是比较方法和交换方式

III.注意qsort参数中void*的用法

(1)void* 就是无具体类型的指针,可以接收任意类型的指针.
(2)有时候不知道别人给传一个什么类型的地址的时候,我又要把它存起来,这时要创建一个void指针接收地址
(3)void
的指针不能解引用操作
(4)void的指针不能进行++或者–,因为他不知道跳过几个字节
(5)可以将void
类型的指针进行强制转换再解引用

IV.qsort函数的头文件

#include<stdlib.h>

V.测试qsort排序整型数据(qsort默认是升序排序)

#include<stdlib.h>  //qsort函数的头文件
#include<stdio.h>
//cmp_int是qsort函数的使用者提供这个函数
int cmp_int(const void* p1, const void* p2) {
	return *(int*)p1 - *(int*)p2;
}
//打印
void print_arr(int arr[],int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test1() {
	int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	//使用qsort来排序整型数组,这里要提供一个比较函数,这个比较函数能够比较两个整数的大小
	//qsort是默认排序成升序的,如果想要排成降序,将cmp_int函数里面的return *(int*)p1 - *(int*)p2;改为return *(int*)p2 - *(int*)p1;
	qsort(arr, sz,sizeof(arr[0]),cmp_int);//数组名表示首元素第一个地址
	print_arr(arr, sz);
}

int main() {
	test1();
	return 0;
}

代码详解图:

VI.测试qsort排序结构型数据

(1)按照年龄来排序
//打印
void print_arr(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
struct Stu {
	char name[20];
	int age;
};
//按照年龄来比较
int cmp_stu_by_age(const void* p1, const void* p2) {  //p1,p2分别指向结构体数据

	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;//将void*类型的指针转换成结构体指针,但是是临时的,所以要加上()再->访问结构体成员
														  //p1指向的元素等于p2指向的元素,返回0;p1指向的元素<p2指向的元素,这里返回<0的数字;p1指向的元素>p2指向的元素,这里返回>0的数字

}
test2() {
	struct Stu s[] = { {"zhangsan",30},{"lisi",25},{"wangwu",50} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	print_arr(s,sz);
}

int main() {
	test2();
	return 0;
}

执行qsort函数前,结构体数组元素分别为:

执行qsort函数后,结构体数组元素分别为:

(2)按照名字来排序

strcmp的用法:👻👻
阅读可以发现:
当str1等于str2,返回0;当str1大于st2,返回大于0的数字;当str1小于str2,返回小于0的数字.
需要头文件#include<string.h>
在比较的时候,先比较首字母,一样的话比较下一个字母,直到比到不相等或者全部比完

代码展示:

#include<string.h>  //strcmp头文件
#include<stdlib.h>  //qsort函数的头文件
#include<stdio.h>
//打印
void print_arr(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
	printf("\n");
}
//测试qsort排序结构体数据
struct Stu {
	char name[20];
	int age;
};
//按照名字来排序(注意两个名字不能相减,要用strcmp比较)
int cmp_stu_by_name(const void* p1, const void* p2) {  //p1,p2分别指向结构体数据

	return strcmp(((struct Stu*)p1)->name ,((struct Stu*)p2)->name);//将void*类型的指针转换成结构体指针,但是是临时的,所以要加上()再->访问结构体成员
	test2() {
	struct Stu s[] = { {"zhangsan",30},{"lisi",25},{"wangwu",50} };
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按照名字来排序
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	print_arr(s,sz);
}

int main() {
	test2();
	return 0;
}

}

排序前:

排序后:

3.模拟实现qsort功能

用冒泡排序模拟一下`qsort怎么用

但是我们没有学快速排序的思想,所以我们使用冒泡排序的思想来实现一个类似于qsort这个功能的冒泡排序bubble_sort()
🐇🐇🐇注意qsort底层是快速排序,这里只是模拟一下这个函数的功能

(1)排序整型数据

拆分各部分讲解

test3:
I.首先准备一个数组
II.求元素个数
III.调用:用冒泡排序模拟qsort函数的bubble_sort(待排序数组的第一个元素,待排序数组的元素个数,每个元素的大小,使用者提供的比较大小的函数的地址)
IV.打印

bubble_sort:
I.形式参数(voidbase指向起始位置,size_t num元素的个数,size_t width 宽度这里是4个字节,int (cmp)(const void p1, const void p2)用函数指针来接收使用者创建的比较函数)

用函数指针调用cmp,这里的cmp实质上是一个回调函数

II.内容:
✨确定冒泡排序的趟数
✨一趟内部进行多少对比较
✨比较的时候用使用者创建的比较函数比,将要比较的两个元素的地址传进来

元素的地址怎么表示?
(char*)base + j * width
什么样的元素+1跳过一个字节? char*
所以把base强制转换成char类型的
起始位置base转换为char
指针
跳过(j+1)个元素,每个元素width这么宽,所以跳过的总字节是(j+1)* width个字节,在起始地址的基础上,所以是(char*)base + j * width

✨交换:创建一个交换函数(将这两个元素的起始地址传进来)

参数:创建一个字符指针接收第一个元素的第一个字节char* buf1,创建一个字符指针接收第二个元素的第一个字节char* buf2,创建一个宽度变量来接收一个元素总共有多少个字节int width
交换的时候一个字节一个字节的交换
用width控制交换多少遍,在循环内部创建临时变量tmp作为中间变量与buf1,buf2进行交换

比较函数:(下图所示默认为升序)
如果是降序排列,将p1,p2互换

交换函数:

主函数:

完整代码展示
//打印
void print_arr(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
}
//比较函数
int cmp_int(const void* p1, const void* p2)
	{
		return *(int*)p1 - *(int*)p2;
	}
//交换
void Swap(char* buf1, char* buf2, int width) {
	int i = 0;
	for (i = 0; i < width; i++) {
		char tmp = *buf1;//先把buf1内指向的第一个字节放入到tmp里面,
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;  //找buf1的下一个字节
		buf2++;  //找buf2的下一个字节
	}
}
//模拟qsort函数的bubble_sort
void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* p1, const void* p2)){
	size_t i = 0;   //将int类型的i改为无符号类型,不然在下面i < num - 1处会有小问题,因为num是size_t类型,i是整型类型
	for (i = 0; i < num - 1; i++) {
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++) {  //这个for循环确定一趟比多少对
		if (cmp(((char*)base + j * width), (char*)base + (j + 1) * width) > 0)    //跳过(j+1)个元素,每个元素width这么宽,所以跳过的总字节是(j+1)*width个字节,在起始地址的基础上,所以是(char*)base + j * width
			{   //前面的元素大于后面的元素
			   //交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
//排序一个整形数组
void test3() {
	int arr[] = { 3,4,5,6,1,2,8,7,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}


int main() {
	test3();
	return 0;
}
	
运行结果

注意点:

注意各个函数在定义时候的先后顺序,只有在前面定义了,后面才可以使用

(2)排序结构体类型数据

创建结构体:

测试按照年龄排序代码展示
//测试qsort排序结构体数据
struct stu {
	char name[20];
	int age;
};
//按照年龄来比较
int cmp_stu_by_age(const void* p1, const void* p2) {  //p1,p2分别指向结构体数据

	return ((struct stu*)p1)->age - ((struct stu*)p2)->age;//将void*类型的指针转换成结构体指针,但是是临时的,所以要加上()再->访问结构体成员
														  //p1指向的元素等于p2指向的元素,返回0;p1指向的元素<p2指向的元素,这里返回<0的数字;p1指向的元素>p2指向的元素,这里返回>0的数字

}
//交换
void Swap(char* buf1, char* buf2, int width) {
	int i = 0;
	for (i = 0; i < width; i++) {
		char tmp = *buf1;//先把buf1内指向的第一个字节放入到tmp里面,
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;  //找buf1的下一个字节
		buf2++;  //找buf2的下一个字节
	}
}
//模拟qsort函数的bubble_sort
void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* p1, const void* p2)){
	size_t i = 0;   //将int类型的i改为无符号类型,不然在下面i < num - 1处会有小问题,因为num是size_t类型,i是整型类型
	for (i = 0; i < num - 1; i++) {
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++) {  //这个for循环确定一趟比多少对
		if (cmp(((char*)base + j * width), (char*)base + (j + 1) * width) > 0)    //跳过(j+1)个元素,每个元素width这么宽,所以跳过的总字节是(j+1)*width个字节,在起始地址的基础上,所以是(char*)base + j * width
			{   //前面的元素大于后面的元素
			   //交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
//打印
void print_arr(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
}
	int cmp_int(const void* p1, const void* p2)
	{
		return *(int*)p1 - *(int*)p2;
	}


void test4() {
	struct stu s[] = { {"zhangsan",30},{"lisi",25},{"wangwu",50} };
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按照年龄来排序
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	print_arr(s, sz);
}

int main() {
	test4();
	return 0;
}

按年龄排序前:

按年龄排序后:

测试按照名字排序代码展示
struct stu {
	char name[20];
	int age;
};
//按照名字来排序(注意两个名字不能相减,要用strcmp比较)
int cmp_stu_by_name(const void* p1, const void* p2) {  //p1,p2分别指向结构体数据

	return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);//将void*类型的指针转换成结构体指针,但是是临时的,所以要加上()再->访问结构体成员
														  //p1指向的元素等于p2指向的元素,返回0;p1指向的元素<p2指向的元素,这里返回<0的数字;p1指向的元素>p2指向的元素,这里返回>0的数字

}
void Swap(char* buf1, char* buf2, int width) {
	int i = 0;
	for (i = 0; i < width; i++) {
		char tmp = *buf1;//先把buf1内指向的第一个字节放入到tmp里面,
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;  //找buf1的下一个字节
		buf2++;  //找buf2的下一个字节
	}
}
//模拟qsort函数的bubble_sort
void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* p1, const void* p2)){
	size_t i = 0;   //将int类型的i改为无符号类型,不然在下面i < num - 1处会有小问题,因为num是size_t类型,i是整型类型
	for (i = 0; i < num - 1; i++) {
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++) {  //这个for循环确定一趟比多少对
		if (cmp(((char*)base + j * width), (char*)base + (j + 1) * width) > 0)    //跳过(j+1)个元素,每个元素width这么宽,所以跳过的总字节是(j+1)*width个字节,在起始地址的基础上,所以是(char*)base + j * width
			{   //前面的元素大于后面的元素
			   //交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
//打印
void print_arr(int arr[], int sz) {
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d ", arr[i]);
	}
}
	int cmp_int(const void* p1, const void* p2)
	{
		return *(int*)p1 - *(int*)p2;
	}


void test4() {
	struct stu s[] = { {"zhangsan",30},{"lisi",25},{"wangwu",50} };
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按照年龄来排序
	//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	//测试按照名字来排序
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	print_arr(s, sz);
}

int main() {
	test4();
	return 0;
}


按名字排序前:

按名字排序后:

(3)用flag优化

✨比如说在中间某个过程冒泡排序已经得到最终排序结果,剩下的排序过程就没有必要再进行
✨这时使用flag来优化
✨假设每一趟排序已经是有序的,flag置为1,要写在循环里面,每一趟都认为是有序的
✨只要进入交换函数进行交换,flag就会被更改为0,如果这一趟里面没有进行交换,flag不会被修改,说明已经是有序的了,不需要再排序
✨判断flag的值是多少,如果是1的话,跳出循环,结束排序

注意👻👻👻
(1)以上是用冒泡排序实现了一个通用的冒泡排序,只不过是这个通用的冒泡排序与qsort的使用是一模一样的
不要认为上面的代码是用冒泡排序模拟qsort,qsort底层用的是快速排序的思想
(2)正是由于回调函数的使用,才可以用函数指针去调用函数去完成不同的功能,使得冒泡排序能够适配更多的类型,这就是回调函数的好处


总结

指针进阶(中)的内容就到这里啦,创作不易如果对友友们有帮助的话,记得点赞收藏博客,关注后续的指针进阶下集内容哦~👻👻👻

有关C语言指针进阶(中)的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  3. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  4. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  5. ruby 变量作为同一对象(指针?) - 2

    >>a=5=>5>>b=a=>5>>b=4=>4>>a=>5如何将“b”设置为实际的“a”,以便在示例中,变量a也将变为4。谢谢。 最佳答案 classRefdefinitializeval@val=valendattr_accessor:valdefto_s@val.to_sendenda=Ref.new(4)b=aputsa#=>4putsb#=>4a.val=5putsa#=>5putsb#=>5当您执行b=a时,b指向与a相同的对象(它们具有相同的object_id).当你执行a=some_other_thing时,a将指向

  6. ruby - 如何保持我不常用的编程语言技能 - 2

    关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭11年前。Improvethisquestion我不经常使用ruby​​-通常它加起来相当于每两个月或更长时间编写一次脚本。我的大部分编程都是使用C++进行的,这与ruby​​有很大不同。由于我与ruby​​之间的差距如此之大,我总是忘记语言的基本方面(比如解析文本文件和其他简单的东西)。我想每天练习一些基本的东西,我想知道是否有一些我可以订阅的网站,并且会向我发送当天的Ruby问题或类似的东西。有人知道这样的站点/Internet服务吗?

  7. ruby-on-rails - 如果特定语言环境中缺少翻译,如何配置 i18n 以使用 en 语言环境? - 2

    如果特定语言环境中缺少翻译,如何配置i18n以使用en语言环境翻译?当前已插入翻译缺失消息。我正在使用RoR3.1。 最佳答案 找到相似的question这里是答案:#application.rb#railswillfallbacktoconfig.i18n.default_localetranslationconfig.i18n.fallbacks=true#railswillfallbacktoen,nomatterwhatissetasconfig.i18n.default_localeconfig.i18n.fallback

  8. ruby-on-rails - 如何通过 URL 更改语言环境? - 2

    在我的双语Rails4应用程序中,我有一个像这样的LocalesController:classLocalesController用户可以通过此表单更改其语言环境:deflocale_switcherform_tagurl_for(:controller=>'locales',:action=>'change_locale'),:method=>'get',:id=>'locale_switcher'doselect_tag'set_locale',options_for_select(LANGUAGES,I18n.locale.to_s)end这有效。但是,目前用户无法通过URL更改

  9. ruby - 一种语言如何被自身解释(如 Rubinius)? - 2

    我使用Ruby编程已经有一段时间了,现在只使用Ruby的标准MRI实现,但我一直对我经常听到的其他实现感到好奇。前几天我在读有关Rubinius的文章,这是一个用Ruby编写的Ruby解释器。我试着在不同的地方查找它,但我很难弄清楚这样的东西到底是如何工作的。我在编译器或语言编写方面从来没有太多经验,但我真的很想弄明白。一门语言究竟如何才能被自己解释?编译中是否有一个我不明白这有意义的基本步骤?有人可以像我是个白痴一样向我解释这个吗(因为无论如何这都不会太离谱) 最佳答案 它比你想象的要简单。Rubinius并非100%用Ruby编

  10. ruby-on-rails - ruby 真的是一种完全面向对象的语言吗? - 2

    Ruby是完全面向对象的语言。在ruby​​中,一切都是对象,因此属于某个类。例如5属于Objectclass1.9.3p194:001>5.class=>Fixnum1.9.3p194:002>5.class.superclass=>Integer1.9.3p194:003>5.class.superclass.superclass=>Numeric1.9.3p194:005>5.class.superclass.superclass.superclass=>Object1.9.3p194:006>5.class.superclass.superclass.superclass.su

随机推荐