C语言摘要 -- K&R笔记(3)
来源:互联网 发布:精子优化处理检查 必要 编辑:程序博客网 时间:2024/06/05 09:45
本文是对《K&R》第五章《Pointer and Arrays》的摘要。
1. 指针是一种数据类型,其存储的内容是其它变量的内存地址,指针的合法值是合法内存地址或0(NULL)。一元操作符&与*用于指针操作:
&:该操作符作用于变量,可理解为‘address-of’, &p即表示p的地址,由于改操作符的结果是得到一个内存地址,所以改操作符只能用于内存中的变量与数组元素,不能用于表达式(expression),常量和寄存器变量(register修饰的变量);
*:该操作符作用于指针,可理解为‘the value pointed by this pointer',*p即表示指针p所指对象的值。
2. 指针与数组:
在C中指针与数组有紧密的联系,如果在C中声明了一个数组int a[5],那么编译器会分配一段连续的内存用来存储这5个int数组变量,而a就是整个内存块的起始地址,即第一个变量a[0]的地址,所以a 与 &a[0]是等价的,可以将其赋值给任何int类型的指针,如:
#include <stdio.h>int main (int argc, char const* argv[]){ int a[5] = {1,2,3,4,5}; int *p = a; if(a == &a[0]) printf("yes!\n"); printf("%d, %d, %d\n", a[0], *a, *p); return 0;}编译后结果为:
$ ./a.out yes!1, 1, 1
3. 指针地址运算:
1). 比较运算:因为指针的值是内存地址或0,所以算术比较运算符(==, !=, <=, >=, >, <)可用于指针,但通常只有在比较属于同一数组的元素地址时才有意义,可以通过此方式判断数组中元素的先后顺序;
2). 加法运算:指针可以与常量数值做加法运算,p + N表示当前p所指元素后面的第N个同类型元素地址,编译器通过指针p的数据类型知道每个元素的大小,例如int a[10], 那么a + 3表示&a[3];
3). 减法运算:指针与常量数值做减法运算时,其意义与加法类似但方向相反,例如int a[10], 那么(&a[9] - 5)表示&a[4];指针也可以与指针类型做减法,其结果表示两个地址之间包含了多少个同类型的变量:
#include <stdio.h>int main (int argc, char const* argv[]){ int a[10] = {1,2,3,4,5,6,7,8,9,10}; printf("%lu\n", (&a[9] - a)); return 0;}结果为:
$ ./a.out 9同理,减法运算通常也只有作用于属于同一数组中的元素时才有意义。
4. 字符串:
C中没有字符串类型,字符串即是char类型的指针,编译器内部表示为以字符'\0'结尾的字符串数组,字符'\0'用于帮助程序找到字符串的结尾。
5. 指针数组:即数组内每个元素是指向声明类型的指针,如char*argv[]中,argv[i]表示的是一个字符串。
6. 多维数组:以二维数组为例进行说明,int a[rows][cols]的声明中,cols必须显示声明大小,不管是否对该数组进行初始化;在函数的形参中,二维数组的cols也必须显示指定大小:
#include <stdio.h>#define ROW 2#define COLS 10void print_mularray(int [][COLS] , int len);int main (int argc, char const* argv[]){ int a[][COLS] = { {1,2,3,4,5,6,7,8,9,10}, {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10} }; print_mularray(a, ROW); return 0;}void print_mularray(int a[][COLS], int len){ int i = 0; for(; i < len; i ++) { int j = 0; for(; j < COLS; j ++) printf("a[%d][%d]: %d\n", i, j, a[i][j]); }}
那么为什么在形参中和在数组声明中,必须指定cols的大小呢?看如下示例:
#include <stdio.h>void f(int [][]);int a[][];int main (int argc, char const* argv[]){ return 0;}编译时错误信息:
$ gcc mularray.c mularray.c:3:1: 错误:数组元素的类型不完全mularray.c:4:5: 错误:数组元素的类型不完全编译器抱怨数组元素的类型不完全!首先看一下二维数组的内存布局:从程序视角我们可以将内存看成一个由字节组成的一维数组,其存储二维数组的方式是先存放第一列,再存放第二列并依次类推,如果我们有二维数组int a[2][5] = {{1,2,3,4,5}, {6,7,8,9,0}},那么其在内存中布局为:1234567890那么如果在int a[rows][cols]的数组中求元素a[i][j]的值,其寻址方式为:a + i * sizeof(int [cols]) / sizeof(int) + j,即:a + i * cols + j;可知编译器必须知道cols的值,才能进行寻址,换句话说,编译器必须知道二维数组中每行的大小,才能正确的进行内存分配,而该值是通过cols来指定的。在上例int a[][]的声明中,编译器能够解析出数组a[]中的每个元素是一个int数组,但是不知道该数组的大小,所以抱怨类型不完全。再看下例:
#include <stdio.h>#define ROW 2#define COLS 10void print_mularraies(int (*)[]);int main (int argc, char const* argv[]){ int a[][COLS] = { {1,2,3,4,5,6,7,8,9,10}, {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10} }; print_mularraies(a); return 0;}void print_mularraies(int (*a)[]){ printf("%d", a[1][3]); printf("%d, %d\n", (*a)[1], (*(a + 1))[1]);}编译时错误信息:
$ gcc -Wall pointer.cgcc -Wall pointer.c pointer.c: 在函数‘print_mularraies’中:pointer.c:22:5: 错误:对未指定边界的数组的使用无效pointer.c:23:5: 错误:对未指定边界的数组的使用无效
原理类似,可以理解为:编译器在解析函数的形参int (*a)[]时,知道a是一个指针,指向一个数据类型是int []的对象,但是由于缺乏数组大小的声明,编译器无法获知每个对象占用多少内存,便无法进行a + 1这样的运算;同理a[1][3]等同于(*(a + 1))[3],错误相同。在函数的形参中声明数组的大小即可:
#include <stdio.h>#define ROW 2#define COLS 10void print_mularraies(int (*)[]);int main (int argc, char const* argv[]){ int a[][COLS] = { {1,2,3,4,5,6,7,8,9,10}, {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10} }; print_mularraies(a); return 0;}void print_mularraies(int (*a)[COLS]){ printf("%d\n", a[1][3]); printf("%d, %d\n", (*a)[1], (*(a + 1))[1]);}编译运行结果正确:
$ ./a.out -42, -2
7. 指针数组与多维数组:
同一数据类型的指针数组与多维数组在使用上类同,但在内存分配上并不一样。通过对多维数组的声明,编译器明确知道需要为其分配多少内存,如:
int [2][100];
编译器会为其分配sizeof(int) * 2 * 100个字节,而对于:
int *[100];
编译器只会初始化一个有100个int指针类型的数组,每个指针指向哪里则并没有定义。
一个字符串数组的内存布局示例如下:
8. 函数指针:
C中函数不是变量,但是可定义指向函数的指针,并将其用于赋值、传递给函数以及从函数返回,函数指针的声明格式为:
return-type (*) (arg-list)
使用方式见如下示例:
#include <stdio.h>int runner(int (*)(int, int), int, int);int sum(int, int);int sub(int, int);int main (int argc, char const* argv[]){ int i = 10, j = 5; int r1, r2; r1 = runner((int (*)(int, int))sum, i, j); r2 = runner((int (*)(int, int))sub, i, j); printf("sum: %d; sub: %d\n", r1, r2); return 0;}int runner(int (*f)(int, int), int a, int b){ return (*f)(a, b);}int sum(int a, int b){ return a + b;}int sub(int a, int b){ return a - b;}运行结果为:
$ ./a.out sum: 15; sub: 5
______________ < 本章终了 > -------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||------w | || ||
- C语言摘要 -- K&R笔记(3)
- C语言摘要 -- K&R笔记(1)
- C语言摘要 -- K&R笔记(2)
- C语言摘要 -- K&R笔记(4)
- C语言摘要 -- K&R笔记(5)
- C程序设计语言(K&R)笔记
- C程序设计语言(K&R)学习笔记--4.const小结
- C程序设计语言(K&R)学习笔记--5.extern小结
- C程序设计语言(K&R)学习笔记--8.结构体
- C程序设计语言(K&R)学习笔记--9.输入输出
- The C programing language K&R 笔记
- C程序设计语言(K&R)第一章学习笔记
- C语言的标准(K&R C,ANSI C,C89,C90,C99)
- R语言:k近邻
- bcc32 -c -pr -O2 -C -K -k- -d -3 -r-
- R语言学习笔记(3)
- K&R C 语言 表查找中的结构体说明
- R语言与机器学习学习笔记(分类算法)(1)K-近邻算法
- javadoc生成
- _beginThreadex 创建多线程解读
- volatile-内存屏障-互斥锁等-----非常好!
- java BigDecimal的使用
- MINI2440开发板Qt开发环境搭建
- C语言摘要 -- K&R笔记(3)
- "整数相加溢出"引出的思考
- sql关键字的解释执行顺序
- linux下firefox安装flash插件
- CRT函数解读
- 北京邮电大学程序设计课程设计第二次实验problem 6
- 实现虚拟机和宿主机能正常上网,通信
- 百度地图API之ItemizedOverlay的使用(Android)
- 题目1493:公约数