一、数组
- 给数组中下标为二的元素开始赋值
// C99
char array_char[5] = {[2] = 'o', 'l', 'l'};
for (int i = 0; i < 5; ++i) {
PRINT_CHAR(array_char[i]);
}
二、字符串
- 字符串与字符数组还是有一定区别的。字符串是以\0结尾
三、函数的数组类型参数
- 函数的参数是数组时候,需要传递一个参数为数组长度,传递二维数组的时候,要确定其中一维数组的长度
四、指针
#include <stdio.h>
int main() {
int a;
scanf("%d", &a);//111
int *p = &a;
return 0;
}
内存中每一个部分标了一个地址,以上面的案例为例
- 首先图上可以得到p,&p,a,&a的内容
- p内容里面是包含了a的地址,而&a也是a的地址(说的废话,哈哈哈)。这里为0x65fe14,在内存中查看这边区域的内容为
这里是16进制,转成10进制就是我们输入的111
-
这个时候取&p,也就是p所在的空间,取出来的为0x65fe18
-
那么按照我们之间学过的,内存编号为0x65fe18的区块包含的内容是0x65fe14(a的地址),结果证明确实如此,下图。
果然看理论是看不实在的,还是动手起来自己瞧一瞧来的实在与真实。
五、只读指针变量与只读变量指针
- 记住const具有左结合性就好
- 再记住一点*p是指向空间内容,p是空间本身
(1)当有
int a;
int b;
//const修饰指针*,p的空间不能改变
int * const p = &a;
//p = &b;报错
(2)当有
int a;
int b;
//const修饰int,p的内容不能变
int const *p = &a;
//*p = 11;报错
六、不能使用的指针
- 不能够给指针随便定义一个地址,不能硬编码
- NULL是一个宏,但是不要给NULL指针赋值
- 杜绝野指针,例如用全局指针指向一个会被销毁的自动类型变量
七、特殊指针
- 指针加减数字,其实是加减的指针指向内容的类型字节数的相应倍数,下面是案例
#include <stdio.h>
int main() {
int a = 5;
int *p = &a;
//当前环境下int为4个字节
printf("%d\n",sizeof(int));//4
printf("%hx\n",p);//fe0c
p += 1;
printf("%hx\n",p);//fe10,刚好位移4个字节
double b = 5.0;
double *q = &b;
//当前环境下double为8个字节
printf("%d\n",sizeof(double));//8
printf("%hx\n",q);//fe00
q += 1;
printf("%hx\n",q);//fe08,刚好8个字节
return 0;
}
- 数组也是一个指针,但是不能用来改变其空间,相当于一个 * const
八、左值与右值
- 左值是内存的空间,右值是值
- *p放在左边是空间,放在右边是值
九、动态内存分配
- 需要引入标准库<stdlib.h>
- malloc创建,free销毁
- 记得初始化,避免脏数据
- calloc会先直接清除为0
- realloc是重新分配,但是追加的不会进行清空
- 内存分配失败时候,返回空指针
- free过后请NULL
十、常见指针使用错误
- 忘记释放指针
- 访问已经释放的指针
- 使用超出边界
- 改变内存的指针
十一、函数指针
- 函数有地址,那么函数也有指针
- 和数组一样,函数名其实就是函数的地址
- 可以使用typedef定义函数类型
#include <stdio.h>
#include <stdlib.h>
#include <io_utils.h>
// 1
int *(f1(int, double));
// 2
int (*f2)(int, double);
// 3
int *(*f3)(int, double);
// 4
// int (*f4)(int, double)[];
// 5
// int (*f5)[](int, double);
typedef int (*Func)(int, double);
typedef int Boolean;
typedef int *IntPtr;
typedef int IntArray[];
int Add(int left, double right) {
return (int) (left + right);
}
void InitPointer(int **ptr, int length, int default_value) {
*ptr = malloc(sizeof(int) * length);
for (int i = 0; i < length; ++i) {
(*ptr)[i] = default_value;
}
}
int main() {
int a;
IntPtr p;
IntArray players = {1,3,4,5};
PRINT_HEX(&main);
PRINT_HEX(&InitPointer);
void (*func)(int **ptr, int length, int default_value) = &InitPointer;
func(&p, 10, 0);
InitPointer(&p, 10, 0);
(*func)(&p, 10, 0);
(*InitPointer)(&p, 10, 0);
PRINT_INT_ARRAY(p, 10);
free(p);
Func func1 = &Add;
int result = func1(1, 3.0);
PRINT_INT(result);
return 0;
}
十二、乱七八糟
- c99之后才支持可变长度,所以MSVC中不支持数组长度为一个变量,但是记住gcc做单独的扩展
- c90的数组甚至是const只读属性也是不行的,必须要求使用常量
- clion中使用ctrl+w查看运算符优先级