【读书笔记】c和指针

第三章

1.作用域

1.代码作用域 , {}
2.文件作用域,头文件
3.原型作用域,函数传参
4.函数作用域,goto

2.链接属性
  1. external,外部链接,外部源文件也可以访问。

staic修饰完,external变为internal

  1. internal,内部链接,本源文件内有效
  2. none
3.存储类型

1.普通内存
2.运行时堆栈
3.硬件寄存器

1.代码外声明的变量 -> 静态存储。生命周期:程序一直运行就一直存在,而不是每次代码块开始时创建,代码执行完销毁
2.static关键字可以使类型从自动(生命周期是函数体内)变成静态(退出该函数后,尽管该变量还继续存在,但不能使用它)。自动和静态的作用域相同。
3.register,自动变量的声明,存储再硬件寄存器,特点:访问效率高,所以可以吧程序经常访问的指针存储再寄存器中

第六章 指针

内存和地址

通过指针访问指向的地址过程叫间接访问或解引用

左值

int a; int *d = &a;都是左值
*d = 10 - *d; 10-*d是右值 *d是左值

指针未初始化

int *a; *a = 12;
未初始化的非法指针,会造成"段违例"或"内存错误"访问未分配给程序的内存位置

NULL指针

可以表示某个特定指针目前未指向任何东西。一般在解引用之前,首先要确保它是NULL指针

间接访问

*&a = 25; <⇒ a = 25;
&会产生a的地址,*操作符访问其地址

指针常量

(int)100 = 25;
指值100从整形转变未『指向整形的指针』

指针的指针

int **c;
int a = 12;
int *b = &a;
int **c = &b;
*c访问c指向的位置,是变量b
**c访问的是【*c】所指向的地址,也就是变量a
间接访问:
a ==> 12
b ==> &a
*b ==> a, 12
c ==> &b
*c ==> b, &a
**c ==> 12, *b, a

指针运算

int *p;
p + 1 +1会加上p类型字节的大小

总结
  1. 一个值应该只有一种意思
  2. 如果指针指向任何有意义东西,那么设置未NULL

第八章 数组

8.1 一维数组

  • 数组具有确定数量的元素,而指针只是一个标量值
  • 当数组在表达式中使用时,编译器才会为它产生一个指针常量
  • 数组名是一个指针常量(除了在sizeof操作符和单目操作符&场合下)

例子:

int a[10];
int b[10];
int *c;
c = &a[0];
  • c = &a[0]是一个指向数组第一个元素的指针, 等价于c = a;
  • b = a 是非法的,不能使用一个赋值符把一个数组所有元素复制到另外一个数组,必须使用一个循环复制每一个元素
  • a = c,非法的,因为a的值是个常量,不能被修改
下标引用
*(b + 3)
  • b是指向整形的指针
  • 加法运算,指向数组第一个元素向后移3个整数长度的位置
  • 间接引用访问这个新位置,或者取得那里的值(右值),或者把一个新值存储于该处(左值)。
array[subscript] < == > *(array + (subscript))
  • 除优先级外,下标引用和间接访问完全相同

例子:

int array[10];
int *ap = array + 2;
  • ap ==> array + 2 ⇒ &array[2]
  • *ap ==> *(array + 2) ==> array[2]
  • ap[0] ==> *(array + 2) ⇒ array[2]
  • ap + 6 ⇒ &array[8] ==> array + 8
  • *ap + 6 ⇒ array[2] + 6(间接访问的优先级高)
  • *(ap + 6) ⇒ array[8]
  • ap[6] ⇒ array[8]
  • &ap 合法,但无法预测编译器会把ap放在array的什么位置
  • ap[-1] ⇒ array[1]
指针和下标
  • 下标更容易理解
  • 不要为了效率上的细微差别而牺牲可读性
  • 当根据某个固定数目的增量在一个数组中移动时,使用指针变量比使用下标产生效率更高
指针和数组
int a[5];
int *b;
  • 表达式*a是合法的,b是非法的(因为b是一个随机值)
  • b++可以编译成功,a++不行(a的值是个常量)
作为函数参数的数组名
  • 数组名就是一个指向数组第一个元素的指针
  • 传递给函数的是一份该指针的拷贝
  • 所有传递给函数的参数都是通过传值方式进行的,但数组名参数是通过地址传递的,下标引用实际执行的就是间接访问
声明数组参数
int strlen(char *string);
int strlen(char string[]);
  • 数组形参可以于任何长度的数组匹配 —— 它实际传递的只是指向数组第一个元素的指针
  • 另一方面,这种实现方法使函数无法知道数组的长度,如果函数需要知道数组的长度,他必须作为一个显示的参数传递给函数
自动计算数组长度

int vector[] = {1,2,3,4,5}

  • 如果未声明数组长度,编译器会自动设置长度。
字符数组初始化
// 字符数组的元素
char message[] = {'h', 'e', 'l', 'l', 'o'};
// 一个真正的字符串常量
char message[] = "hello";
8.2 多维数组
int c[2][4];
int d[3][6][10];
  • 数组d可以看成3排,6行10列的整形三维数组
指向数组的指针
//合法,vp指针指向 整形的指针
int vector[10], *vp = vector;

// 非法m, m是指向整形数组的指针
int m[3][10], *mp = matrix;
// 正确应该这样
int (*p)[10];
作为参数传递的多维数组
void func1(int (*mat)[10]);
void func2(int mat[][10]);
  • 第一维的长度并不需要,因为计算下标值时用不到它,但需要知道第二个及以后各维长度
总结
  • 可读性几乎总大于程序运行的效率
  • 函数的指针形参都应该声明为const
  • 有些环境,register关键字提高程序的运行时效率
  • 多维数组用花括号,提高程序可读性

第九章 字符串、字符和字节

不受限的字符串函数
复制字符串
char *strcpy(char *dst, char const *src);
  • 注意:src长度如果大于dst长度会出core(abort)
连接字符串
char *strcat(char *dst, char const *src);
字符串比较
int strcmp(char const *s1, char const *s2);
长度受限的字符串函数
char *strncpy(char *dst, char const *src, size_t len);
char *strncat(char *dst, char const *src, size_t len);
char strncmp(char const *s1, char const *s2, size_t len);
字符串查找基础
查找一个字符
char *strchr(char const *str, int ch);
chra *strrchr(char const *str, int ch);
查找任意几个字符
char *strpbrk(char const *str, char const *group);
查找一个子串
char *strstr(char const *s1, char const *s2);
查找一个字符串的前缀
size_t strspn(char const *str, char const *group);
size_t strcspn(chat const *str, char const *group);
内存操作
void *memcpy(void *dst, void const *src, size_t length);
void *memmove(void *dst, void const *src, size_t length);
void *memcmp(void const *a, void const *b, size_t length);
void *memchr(void const *a, int ch, size_t length);
void *memset(void *a, int ch, size_t length);
总结
  • 应该在使用有符号数的表达式中使用strlen函数
  • 在表达式中混用有符号和无符号数
  • 使用strcpy函数把一个长字符串复制到一个较短的数组中,导致数组溢出
  • 使用strcat函数把一个字符串添加到一个数组中,导致数组溢出
  • 把strcmp函数的返回值当做bool判断
  • 把strcmp函数的返回值与1和-1进行比较
  • 使用并非以NUL字节结尾的字符序列
  • 使用strncpy函数和strxxx族函数混用
  • 忘记strtok函数将会修改它所处理的字符串
  • strtok函数是不可再入的

第十章 结构和联合

直接访问

点操作符(.)

间接访问

箭头操作符(->)

结构体的子引用

1.错误用法,会永无止境递归下去

struct test1 {
	int a;
	stuct test1 b;
	int c;
};

2.正确用法,现在的b是一个指针而不是一个结构

struct test2 {
	int a;
	stuct test2 *b;
	int c;
};

3.错误示例,类型名知道声明的末尾才定义

typedef struct  {
	int a;
	stuct test3 *b;
	int c;
} test3;
不完整的声明

需求:声明一些相互之前存在依赖的结构,其中一个结构包含了另外一个结构的一个或多个成员

struct B;
struct A {
	struct B *partner;
};

struct B {
	struct A *partner;
};
结构指针和成员
typedef struct {
	int a;
	short b[2]
} EX2;
typedef struct EX {
	int a;
	char b[3];
	EX2 c;
	struct EX *d;
} EX;

EX x = {10, "HI", {1, {-1, 23},}, 0}
EX *px = &x;

在这里插入图片描述

访问结构成员

// ->优先级大于&,所以不需要使用括号
pi = &px->x;

在这里插入图片描述

px->b[1];

在这里插入图片描述

px->c.a

e3f5871b64693b339d72504fc59f971.png)

px->d->c.b[1]

请添加图片描述

作为函数参数的结构

通常情况下传递指针比较高效

typedef struct  {
	char p[];
	int quantity;
	float unit_price;
	float total;
}Transation;
void test(register Transation *trans) {
	trans->total = trans->quantity *trans->unit_price;
}
// 调用
test(&current_trans);
联合

联合的所有
54w1 1 成员引用的是内存中的相同位置,如果想在不同时刻把不同的东西存储再同一个位置,就用联合

union {
	float f;
	int i;
} fi;

第十一章 动态内存分配

malloc和free

malloc函数从内存池中提取一块合适的内存,返回一个指向这块内存的指针,但是不会初始化。分配失败返回NULL指针
calloc会初始化
free函数会把内存归还给内存池

void *malloc(size_t size);
void free(void *pointer);
calloc和realloc
void *calloc(size_t num_elements, size_t elements_size);
void  realloc(void *ptr, size_t new_size);
  • calloc参数包括所需元素的数量和每个元素的字节数,根据这些值,他能计算出总共所需分配的内存
  • realloc函数 可以、扩大或缩小已经分配的内存块大小。如果扩大,在后面增加新的内存块。如果缩小,就把内存尾部的内存拿掉,剩余部分内存的原先内存保留。
常见的动态内存错误
  1. NULL指针的解引用
  2. 对分配内存进行操作时越界
  3. 释放并非动态分配的内存
  4. 试图释放一块动态分配内存的一部分
  5. 内存被释放后继续使用

第十三章 高级指针话题

指向指针的指针
int i;
int *p
int **ppi;

1. printf("%d\n", ppi);  // 自动变量没有被初始化,打印一个随机值。如果是静态变量结果是0
2. printf("%d\n", &ppi);  // 把ppi的地址,以10进制的形式打印
3. *ppi = 5;  // 结果不能预测,因为ppi没有被初始化,所有也不应该执行间接访问

ppi = &pi;  // 把ppi初始化为指向p的变量
*ppi = &i; // 把pi初始化为指向变量i

i = 'a';
*pi = 'a';
**ppi = 'a'

为什么要对指针间接访问:在某些函数,变量名在函数的作用域内部是未知的,函数拥有的只是一个指向需要修改的内存位置的指针,所以要通过对指针间接访问来修改变量

高级声明
int f;  //整形变量	
int *f;	//	指向整形的指针

int f();	// f是个函数,返回值是一个整数
int *f(); 	//	f是个函数,返回值是一个指向整形的指针

int (*f)(); // f是一个函数指针,指向的函数返回一个整型值
int *(*f)(); // f是一个函数指针,指向的函数返回一个整形指针

int *f[];	// f是一个数组,他的元素类型指向整形的指针
int f()[]; 	//f是一个函数,返回标量值,不能返回数组
函数指针,回调函数
Node *search_list(Node *node, int const value) {
	while (node != NULL) {
		if (node->value == value) break;
		node = node->link;
	}
	return node;
}

这个函数只适用值的整数链表,如果在一个字符串链表查询就需要另外编写函数。
解决方案就是函数指针,把一个指向这个函数的指针作为参数传递给查找函数,然后查找函数调用这个函数来执行值的比较。
回调函数:用户把一个函数指针作为参数传递给其他函数,后者将『回调』用户函数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值