目录
十、字符串和指针
字符串:字符串是以‘\0’ 结尾的若干字符的集合
字符串存储形式:数组、字符串指针、堆
char string[100] = "i love c!";
定义一个字符数组string用来存放多个字符
char *str = "i love c!";
定义一个指针变量str,只能存放字符地址编号,这个字符串的字符不能存放在str指针变量中。
str只是存放了字符1的地址编号, "i love c!"存放在文字常量区
char *str = (char *)malloc(10 * sizeof(char));
动态申请10个字节存储空间,首地址给str
strcpy(str, "i love c!")
总结:
- 字符数组: 在内存(栈、静态全局区)中开辟一段空间存放字符串
- 字符串指针:在文字常量区开辟了一段空间存放字符串,将字符串的首地址付给str
- 堆:使用malloc函数在堆区申请空间,将字符串拷贝到堆区
注意:
- 可修改性
- 栈和全局区内存中的内容是可修改的
char str[100] = 'i love c!'; str[0] = 'y';//可修改
- 文字 常量区的内容不可修改(尽量少使用)
char *str = "i love c!"; *str = 'y'; // 错误,存放在文字常量区,不可修改
- 堆区内容可以修改
char *str = (char *)malloc(10 * sizeof(char)); 动态申请10个字节存储空间,首地址给str strcpy(str, "i love c!") *str = 'y'; // 正确可以修改,因为堆区内容可修改
注意:str 指针指向的内存能不能被修改,要看str指向那里,
str指向文字常量区时,内存里的内容不可修改
str指向栈、堆、静态全局区时,内存的内容是可以修改的
初始化:
- 字符数组、指针指向的字符串:定义时直接初始化
char buf[] = "hello world"
char *buf_point = "hello world"
- 堆中存放的字符串不能初始化,只能使用strcpy、scanf赋值
char *buf_heap;
buf_heap = (char *)malloc(15)
strcpy(buf_heap, "hello world")
scanf("%s", buf_heap);
使用时赋值
字符数组:使用scanf或strcpy
char buf_aver[128];
buf_aver = "hello !"; // 错误,因为字符数组名字是常量
strcpy(buf_aver, "hello!"); // 正确
scanf("%s", buf_aver); // 正确
指向字符串指针:
char *buf_point;
buf_point= "hello !"; // 正确,buf_point指向另外一个字符串
strcpy(buf_point, "hello!"); // 错误,只读,能不能复制字符串到buf_point指向的内存里取决于buf_point指向哪里
十一、 数组指针
11.1 二维数组
二维数组,有行有列。二维数组可以看成多个一维数组构成,是多个一维数组的集合,可认为二维数组的每一个元素是个一维数组。
int a[3][5];
可以将二维数组认为行指针
11.2 数组指针
数组指针是个指针,指向一个数组,加1 跳一个数组,即指向下个数组。
数组指针的作用:可以保存二维数组的首地址。
11.3 定义
指向数组类型 : (*指针变量名) [指向的数组的元素个数]
int (*p)[5]; //定义一个数组指针变量p,p指向整型的5个元素数组
p+1 往下指5个整型,跳过一个有5个整型元素的数组
#include <stdio.h>
void test1()
{
int a[3][5];
int(*p)[5];
printf("a = %p\n", a);
printf("a + 1 = %p\n", a + 1);
p = a;
printf("p = %p\n", a);
printf("p + 1 = %p\n", p + 1);
}
void main()
{
test1();
}
11.4 用途
//可以将二维数组的首地址传递到另一个函数里面,此时函数的形参 就需要 定义为数组指针
void fun(int (*p)[5], int x, int y)
{
p[0][1] = 101;
}
void test2()
{
int i, j;
int a[3][5] = {0};
fun(a, 3, 5);
for(i = 0; i < 3; i++)
{
for(j = 0; j < 5; j++)
{
printf(" %d ", a[i][j]);
}
printf("\n");
}
}
void main()
{
test2();
}
11.5 各种二维指针的定义
- 一维数组指针,加1后指向下个一维数组
int (*p)[5];
配合每行有5个int型元素的二维数组来用
int a[3][5]
int b[4][5]
int c[5][5]
int d[6][5]
.......
p = a;
p = b;
p = c;
p = d;
- 二维数组指针,加1后指向下个一维数组
int (*p)[4][5]
配合三维数组来用,三维数组中由若干个4行5列数组构成
int a[3][4][5]
int b[4][4][5]
int c[5][4][5]
int d[6][4][5]
.......
p = a;
p = b;
p = c;
p = d;
.................以此类推
- 注意:
容易混淆的内容:
指针数组:是个数组,若干个相同类型的指针构成的集合
int *p[10];
数组p有10个int*类型的指针变量构成
数组指针:本身是个指针,指向一个数组,加1跳一个数组int (*p)[10];
p是个数组指针,p加1 指向下个数组,跳10个整型/
指针的指针:int **p;
int *q;
p = &q;
11.6 数组名字取地址 —— 数组指针
一维数组名字取地址——一维数组指针,即加1跳一个一维数组
int a[10];
a + 1 跳一个整型元素,是a[1] 的地址
a 和 a + 1 相差 一个元素, 4个字节
&a 为一个一维数组指针,是int(9p) [10]类型的
(&a) + 1 和&a 相差一个数组即10个元素即40个字节
void main()
{
int a[10];
printf("a =%p\n", a );
printf("a + 1 =%p\n", a + 1);
printf("&a=%p\n", &a);
printf("&a + 1=%p\n", &a + 1);
}
11.7 数组名字和指针变量的区别
int a[10];
int *p;
p = a;
相同点:
a是数组的名字,是a[0]的地址,p = a即保存了a[0] 的地址,即 a和p都指向a[0],在引用数组元素时,a 和 p 等价
a[2], *(a + 2), p[2], *(p + 2)都是对数组a中a[2]元素的引用
不同点:
- a是常量,p是变量 ,可以用等号“ = ”给p赋值,但不能用等号给a赋值
- 对a 取地址,和对p取地址结果不同,因为a是数组的名字,所以对a取地址结果为数组指针,p是指针变量,所以对p取地址(&p)结果为指针的指针
11.8 多维数组中指针的转换
在二维数组中,行地址取 * 不是取值的意思,而是指针降级意思,由行地址(数组地址)变成这一行第 0 个元素的地址。取 * 前后还是指向同一个地方,但指针的类型不一样
//二维数组的数组名降级问题
//二维数组的数组名默认是一个行指针,加1保存下一行的首地址
//二维数组的数组名取 * ,表示地址的 降级,意味着行指针降级为列地址,加1保存下一元素的地址
//一维数组的数组名默认是一个列指针,加1保存下一个元素的地址
//一维数组的数组名取&,则是地址的升级, 将列指针升级为行指针,加1 保存下一行元素的首地址
void test3()
{
int a[3][5] = {0};
printf("a = %p\n", a);
printf("a + 1 = %p\n", a + 1);
printf("*a = %p\n", *a);
printf("(*a) + 1 = %p\n", (*a) + 1);
}
void main()
{
test3();
}