目录
1.数组名的含义
2.数组下标
3.字符串常量(匿名数组)
4.零长数组(结构体空间扩充)
5.变长数组
6.char型指针
7.多级指针
8.指针万能拆解法
9.void型指针
10.const型指针
11.函数指针巧用
12.练习题
1.字节序
2.数组下标运算、指针运算、内存操作
3.复杂声明,多维数组操作
4.数组操作
5.复制数组
6.字符去重
数组名a有两个含义:
第一含义是:整个数组(在数组定义中 || 在 sizeof 运算表达式中 || 在取址符&中)
第二含义是:首元素地址(其他任何情形下,即指向首元素的指针)
实例:
int a[3]; // 此处,a 代表整个数组
printf("%d\n", sizeof(a)); // 此处,a 代表整个数组
printf("%p\n", &a); // 此处,a 代表整个数组,此处为整个数组的地址
int *p = a; // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
p = a + 1; // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
function(a); // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
scanf("%d\n", a); // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
注意:C语言只有在第一含义的场合下表现为数组,其他大部分场合都表现为首元素的地址,当数组表现为首元素地址时,实际上就是一个指向其首元素的指针。数组运算实际上就是指针运算。
数组下标实际上是编译系统的一种简写.
其等价形式是: a[i] = 100; 等价于 *(a+i) = 100;
根据加法交换律,以下的所有的语句均是等价的:
a[i] = 100;
*(a+i) = 100;
*(i+a) = 100;
i[a] = 100;
数组运算,等价于指针运算
例如:
int a[3];
// 此处的a等价于 &a[0],而a[0]是一个int,因此此处a的类型是 int (*)
int *p = a+1;
int a[2][3];
// 此处的a等价于 &a[0],而a[0]是一个int[3],因此此处a的类型是 int (*)[3]
int (*p)[3] = a+1;
int *a[3];//相当于二级指针,int **a;
// 此处的a等价于 &a[0],而a[0]是一个int *,因此此处a的类型是 int (*)*
int **p = a+1;
字符串常量在内存中的存储,实质是一个匿名数组
匿名数组,同样满足数组两种涵义的规定
实例:
printf("%d\n", sizeof("abcd")); // 此处 "abcd" 代表整个数组
printf("%p\n", &"abcd"); // 此处 "abcd" 代表整个数组
printf("%c\n", "abcd"[1]); // 此处 "abcd" 代表匿名数组的首元素地址
char *p1 = "abcd"; // 此处 "abcd" 代表匿名数组的首元素地址
char *p2 = "abcd" + 1; // 此处 "abcd" 代表匿名数组的首元素地址
用途:放在结构体的末尾,作为可变长度数据的入口
实例:
struct node
{
/* 结构体的其他成员 */
// 成员1
// 成员2
// ... ...
int len;
char *data[0];
};
// 给结构体额外分配 10 个字节的内存。
struct node *p = malloc(sizeof(struct node) + 10);
p->len = 10;
// 额外分配的内存可以通过 data 来使用
p->data[0] ~ p->data[9]
概念:定义时,使用变量作为元素个数的数组。
要点:变长数组仅仅指元素个数在定义时是变量,而绝非指数组的长度可长可短。实际上,不管是普通数组还是所谓的变长数组,数组一旦定义完毕,其长度则不可改变。
实例:
int len = 5;
int a[len]; // 数组元素个数 len 是变量,因此数组 a 是变长数组
int x = 2;
int y = 3;
int b[x][y]; // 数组元素个数 x、y 是变量,因此数组 b 是变长数组
int b[2][y]; // 数组元素个数 y 是变量,因此数组 b 是变长数组
int b[x][3]; // 数组元素个数 x 是变量,因此数组 b 是变长数组
语法:变长数组不可初始化,即以下代码是错误的:
int len = 5;
int a[len] = {1,2,3,4,5}; // 数组 a 不可初始化
char型指针实质上跟别的类型的指针并无本质区别,但由于C语言中的字符串以字符数组的方式存储,而数组在大多数场合又会表现为指针,因此字符串在绝大多数场合就表现为char型指针。
定义:
char *p = "abcd";
如果一个指针变量 p1 存储的地址是另一个普通变量 a 的地址,那么称 p1 为一级指针
如果一个指针变量 p2 存储的地址,是指针变量 p1 的地址,那么称 p2 为二级指针
如果一个指针变量 p3 存储的地址,是指针变量 p2 的地址,那么称 p3 为三级指针
以此类推,p2、p3等指针被称为多级指针
示例:
int a = 100;
int *p1 = &a; // 一级指针,指向普通变量
int **p2 = &p1; // 二级指针,指向一级指针
int ***p3 = &p2; // 三级指针,指向二级指针
任意的指针,不管有多复杂,其定义都由两部分组成。
第1部分:指针所指向的数据类型,可以是任意的类型
第2部分:指针的名字
实例:
char (*p1); // 第2部分:*p1; 第1部分:char;
char *(*p2); // 第2部分:*p2; 第1部分:char *;
char **(*p3); // 第2部分:*p3; 第1部分:char **;
char (*p4)[3]; // 第2部分:*p4; 第1部分:char [3];
char (*p5)(int, float); // 第2部分:*p5; 第1部分:char (int, float);
注意:上述示例中,p1、p2、p3、p4、p5本质上并无区别,它们均是指针
上述示例中,p1、p2、p3、p4、p5唯一的不同,是它们所指向的数据类型不同
第1部分的声明语句,如果由多个单词组成,C语言规定需要将其拆散写到第2部分的两边
概念:无法明确指针所指向的数据类型时,可以将指针定义为 void 型指针
要点: void 型指针无法直接索引目标,必须将其转换为一种具体类型的指针方可索引目标
void 型指针无法进行加减法运算
void关键字三个作用:
1.修饰指针,表示指针指向一个类型未知的数据。
2.修饰函数参数列表,表示函数不接收任何参数。(预习)int main(void)
3.修饰函数返回类型,表示函数不返回任何数据。(预习)void func(int)
// 指针 p 指向一块 4 字节的内存,且这4字节数据类型未确定
void *p = malloc(4);
// 1,将这 4 字节内存用来存储 int 型数据
*(int *)p = 100;
printf("%d\n", *(int *)p);
// 2,将这 4 字节内存用来存储 float 型数据
*(float *)p = 3.14;
printf("%f\n", *(float *)p);
const型指针有两种形式:①常指针 ②常目标指针
常指针:const修饰指针本身,表示指针变量本身无法修改。
常目标指针:const修饰指针的目标,表示无法通过该指针修改其目标。 常指针在实际应用中不常见。 常目标指针在实际应用中广泛可见,用来限制指针的读写权限
int a = 100;
int b = 200;
// 第1中形式,const修饰p1本身,导致p1本身无法修改
int * const p1 = &a;
// 第2中形式,const修饰p2的目标,导致无法通过p2修改a
int const *p2 = &a;
const int *p2 = &a;
概念:指向函数的指针,称为函数指针。
特点:函数指针跟普通指针本质上并无区别,只是在取址和索引时,取址符和星号均可省略
示例:
void f (int); // 函数 f 的类型是: void (int)
void (*p)(int); // 指针 p 专门用于指向类型为 void (int) 的函数
p = &f; // p 指向 f(取址符&可以省略)
p = f; // p 指向 f
// 以下三个式子是等价的:
f (666); // 直接调用函数 f
(*p)(666); // 通过索引指针 p 的目标,间接调用函数 f
p (666); // 函数指针在索引其目标时,星号可以省略
要点: 函数指针是一类专门用来指向某种类型函数的指针。
函数的类型不同,所需要的函数指针也不同。
函数的类型,与普通变量的类型判定一致,即去除声明语句中的标识符之后所剩的语句。
编写一个程序,测试当前平台的字节序。
解析: 此题有多种解法,在本次课中,我们可以使用指针来操作某个特定的字节。
#include
int main(void){
// 定义一个4字节的整型数据
int a = 0x12345678;
// 定义一个char型指针指向最低地址
char *p = &a;
// 将最低字节数据打印出来
// 如果是0x78,那就代表最低地址存储了低有效位,是小端序
// 如果是0x12,那就代表最低地址存储了高有效位,是大端序
printf("%#x\n", *p);
}
分析以下代码的输出结果
#include
int main(void){
long a[4] = {1, 2, 3, 4};
long *p1=(long *)(&a+1);
long *p2=(long *)((long)a+1);
printf("%lx\n", p1[-1]);
printf("%lx\n", *p2);
return 0;
}
首先,&a是整个数组的地址,因此&a+1实际上是向后偏移了16个字节,因此 p1 指向了数组边界。然后输出 p1[-1] 等价于*(p1-1),因为 p1 是 long 型指针,因此 p1-1 就向前偏移 sizeof(long) 个字节,也就是指向了 a[3]。
其次,(long)a+1 是一个纯整数运算(因为a被强转为long了),因此 p2 就指向了long型数据 a[0] 的第二个字节,最后打印*p2 时,由于 p2 是一个 long 型指针,系统会从 a[0] 的第二个字节开始,取出 sizeof(long) 个字节出来作为 long 型数据来解释,因此最后输出的结果是 a[0] 的高位三字节和 a[1] 低位一字节的数据。
编写程序,声明一个二维 int 型数组 a,再声明另一个一维数组指针数组 b,使该数组 b 的每一个指针分别指向二维数组 a中的每一个元素(即每一个一维数组),然后利用数组 b 计算数组 a 的和。
//声明一个二维 int 型数组 a,再声明另一个一维数组指针数组 b,使该数组 b 的每一个指针分别指向二维数组 a
//中的每一个元素(即每一个一维数组),然后利用数组 b 计算数组 a 的和。
#include
int main(){
int a[2][4]={{1,2,3,4},{5,6,7,8}};
int (*b[2])[4];
b[0]=&a[0];
b[1]=&a[1];
int sum=0;
for(int i=0;i<2;i++){
for (int j = 0; j < 4; j++){
sum=sum+(*b[i])[j];
}
}
printf("数组a的和sum=%d\n",sum);
}
编写一个程序,求一个有N个元素的整型数组中子数组之和的最大值,子数组指的是一个数组中连续的若干个相邻的元素。 例如:
int a[7] = {-2, 5, -1, 6, -4, -8, 6}; 则子数组之和的最大值是 5+(-1)+6,即答案是 10。
解析: 本题是一道笔试题,考的是算法。因此不宜使用三重循环的暴力破解法。 参考代码:
#include
int main(void){
int a[100];
int len = 0;
printf("请输入系列整数,以#结束\n");
while(scanf("%d", &a[len]) != 0)
len++;
int max=a[0], sum=0;
int i;
for(i=0; i max)
max = sum;
else if(sum < 0)
sum = 0;
}
printf("最大子数组之和: %d\n", max);
return 0;
}
【5】编写一个函数,它接收两个 m×n 的整型二维数组 a 和 b,
函数的功能是将数组 a 中的数据复制到数组 b 中。参考代码:
#include
//写一个函数,它接收两个 m×n 的整型二维数组 a 和 b,
//函数的功能是将数组 a 中的数据复制到数组 b 中。
void cv(int m,int n,int a[m][n],int b[m][n]){
for (int i = 0; i < m; i++){
for(int j=0;j
编写一个程序,去掉给定字符串中重复的字符。 例如:
输入:google 输出:gole
// 6、编写一个程序,去掉给定字符串中重复的字符。
// 例如给定”google”,输出”gole”。(华为笔试题)
#include
#include
int main(int argc, char const *argv[])
{
char arr[20];
char ayy[20];//接收没有重复的字符串
printf("请输入一个字符串:\n");
scanf("%s", arr);
//获取arr字符串长度(不包括后面的'\n');
int len = strlen(arr);
//定义统计数据
int sum = 0;
//定义两个指针分别指向他们
char *p = arr;
char *q = ayy;
int k = 0;
// 判断
for (int i = 0; i < len; i++){
sum = 0;
for (int j = 0; j < i; j++){
if (*(p + i) == *(p + j))
sum++;
}
printf("sum = %d\n", sum);
if(sum == 0){
*(q + k) = *(p + i);
k++;
}
}
printf("%s\n", ayy);
return 0;
}