哈喽,我是老吴,继续记录我的学习心得。
每天一点系列是我对微习惯的践行。现在能做到每天一点 C,将来就会有更多的每天一点系列,没人规定嵌入式软件工程师就只能学习 C 语言和折腾 Linux,不要给自己设限。
为什么是每天一点?
养成写作习惯的第一步:放弃追求大段的、固定的、安静的写作时间,培养随时写作的能力。
同样的技巧也能应用到技术学习上,工程师也可以放弃追求大段的、固定的、安静的看书写代码的时间。遇到一个新知识,第一时间找到相关资料,找到切入点并将其记录在自己的技术笔记中,并且定期复查及完善。
一、每天一点C / 数组和指针
正文目录:
1. 数组名是该数组首元素的地址
2. 用指针操作数组
3. 数组和指针的关系密切
4. 编写处理一维数组的函数:传递数组起始地址+数组长度
5. 编写处理一维数组的函数:传递数组起始地址+结束地址
学习环境:
- Ubuntu 16.04
- gcc version 5.4.0
1. 数组名是该数组首元素的地址
演示 demo:
#define MONTHS 12
int main(void){
int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
if (days == &days[0])
printf("TRUE\n");
return 0;
}
运行效果:
$ gcc array_name.c -o array_name
$ ./array_name
TRUE
相关要点:
- 数组名和该数组首元素的地址都是常量,在程序的运行过程中,不会改变。
2. 用指针操作数组
演示 demo:
#define SIZE 4
int main(void){
short dates [SIZE];
short * pti;
short index;
double bills[SIZE];
double * ptf;
pti = dates; // assign address of array to pointer
ptf = bills;
printf("%23s %15s\n", "short", "double");
for (index = 0; index printf("pointers + %d: %10p %10p\n", index, pti + index, ptf + index);
return 0;
}
运行效果:
$ gcc point_array.c -o point_array
$ ./point_array
short double
pointers + 0: 0x7ffc890b17c0 0x7ffc890b17d0
pointers + 1: 0x7ffc890b17c2 0x7ffc890b17d8
pointers + 2: 0x7ffc890b17c4 0x7ffc890b17e0
pointers + 3: 0x7ffc890b17c6 0x7ffc890b17e8
指针的值是它所指向对象的地址,指针前面使用 * 运算符可以得到该指针所指向对象的值。
%p 会以十六进制显示指针的值。
必须声明指针所指向对象类型,原因之一:计算机既要知道储存对象的地址,又要知道储存对象需要多少字节。
指针加 1 指的是增加一个数据存储单元。对数组而言,这意味着加 1 后的地址是下一个元素的地址,而不是下一个字节的地址。
3. 数组和指针的关系密切
以下关系表明了数组和指针的关系十分密切:
days + 2 == &days[2] // TRUE,相同的地址
*(days + 2) == days[2] // TRUE,相同的值
演示 demo:
int main(void){
int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
int index;
for (index = 0; index printf("Month %2d has %d days.\n", index +1, *(days + index));
return 0;
}
运行效果:
$ gcc point_array2.c -o point_array2
$ ./point_array2
Month 1 has 31 days.
Month 2 has 28 days.
...
Month 12 has 31 days.
C 语言在描述数组表示法时借助了指针:ar[n]的意思是*(ar + n)。
可在编写程序时适时使用 数组表示法或指针表示法,这是两种功能等效的方法。大多数情况下可以用指针表示数组,反过来,也可以用数组表示指针。
编译器编译这两种写法生成的代码相同。
4. 编写处理一维数组的函数:传递数组起始地址+数组长度
假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为 data 的 int 类型数组,应该如何调用该函数?
演示 demo:
int sum(int ar[], int n);
int main(void){
int data[SIZE] = {20,10,5,39,4,16,19,26,31,20};
long answer;
answer = sum(data, SIZE);
printf("The total number of data is %ld.\n", answer);
printf("The size of data is %zd bytes.\n",
sizeof data);
return 0;
}
int sum(int ar[], int n){
int i;
int total = 0;
for( i = 0; i total += ar[i];
printf("The size of ar is %zd bytes.\n", sizeof ar);
return total;
}
运行效果:
$ gcc func_array.c -o func_array
func_array.c: In function ‘sum’:
func_array.c:26:53: warning: ‘sizeof’ on array function parameter ‘ar’ will return size of ‘int *’ [-Wsizeof-array-argument]
printf("The size of ar is %zd bytes.\n", sizeof ar);
^
func_array.c:19:13: note: declared hereint sum(int ar[], int n)
$ ./func_array
The size of ar is 8 bytes.
The total number of data is 190.
The size of data is 40 bytes.
数组名是该数组首元素的地址,所以实参 data 是一个储存int 类型值的地址,那么函数 sum 的形参数就应该是一个指针。
在函数声明或函数定义中,int ar[] 等效于 int *ar,本质上都应该看作是指针。
只有在函数声明或函数定义中,才可以用int ar[]代替int *ar。
函数声明可以省略参数名,所以下面 4 种原型都是等价的:
注意编译器的警告信息:warning: ‘sizeof’ on array function parameter ‘ar’ will return size of ‘int *’,这是在提示程序员你打印的是指针的 size,而不是整个数组的 size。
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
- 一般会把数组的参数作为其中一个参数传递给处理数组的函数。
5. 编写处理一维数组的函数:传递数组起始地址+结束地址
演示 demo:
int sump(int * start, int * end);
int main(void){
int data[SIZE] = {20,10,5,39,4,16,19,26,31,20};
long answer;
answer = sump(data, data + SIZE);
printf("The total number of data is %ld.\n", answer);
return 0;
}
int sump(int * start, int * end){
int total = 0;
while (start {
total += *start;
start++;next element
}
return total;
}
运行效果:
$ gcc func_array2.c -o func_array2
$ ./func_array2
The total number of marbles is 190.
相关要点:
注意 answer = sump(data, data + SIZE) 和 while (start < end),这是一种推荐用法,简洁且清晰。因为下标从 0 开始,所以 data + SIZE 指向数组末尾的下一个位置。
data + SIZE 是推荐的,但是 data[SIZE] 则是错误的。
处理数组的函数实际上用指针作为参数,但是在编写这样的函数时,可以选择是使用数组表示法还是指针表示法。数组表示法让函数是处理数组的这一意图更加明显,而对于喜欢指针的程序员,使用指针表示法,则觉得使用指针更自然。
二、思考技术,也要思考人生
我喜欢学习技术,但我更爱学习如何生活。
你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。
如果你也对 嵌入式系统 (Linux、RTOS、OpenWrt、Android) 和 开源软件 感兴趣,并且想和更多人互相交流学习的话,请关注我的公众号:嵌入式Hacker,一起来学习吧。
无论是关注或转发,还是打赏,都是对作者莫大的支持。对了,记得点 在看和点赞 ,如果你还想看到我的推送的话。
祝各位工作顺利,家庭幸福,财源滚滚~
ps:
欢迎加入我的微信群:加我微信,我拉你进群,暗号(加群)。