全国咨询/投诉热线:400-618-4000

c/c++培训C语言核心知识总结(五、六)

更新时间:2016年10月21日16时34分 来源:传智播客C++培训学院 浏览次数:

五、指针 和 数组的用法
 
int num[5] = {10, 20, 30, 40, 50};
int *p = num;
 
打印的值 打印后*p的值是 数组里的原值
// 操作地址
*p++ 10   20 10
*(p++)
*和++的优先级相同,根据结合性(从右往左),那么p先和后自增运算符++结合,
++操作将在表达式完成后进行自增,也就取出p指向的值之后,p指向的下标后移一位(4个字节)。
 
*++p 20   20 10
*(++p)
*和++优先级相同,根据结合性(从右往左),那么p先和前自增运算符++结合,
++操作将会立即完成,p指向的下标后移一位(4个字节),然后再取出p指向的值。
 
 
// 操作数值
(*p)++ 10 11 11
根据优先级()小括号优先级最高,p先和*相结合,然后再和后自增运算符++结合,
因为是后自增,所以先打印当前下标的值,然后在原值的基础上自增 1,此时原值已被改变
 
 
++*p 11 11 11
++(*p)
根据结合性/优先级,*和p先结合,然后再和前自增运算符++结合,
因为是前自增,所以先在原值的基础上自增1,然后在打印这个值,此时原值已被改变。
 
 
总结:如果一个表达式里有多个运算符,则先进行优先级比较,先执行优先级高的运算符;
如果优先级相同,那就看结合性,根据结合方向来做运算。
 
 
结合性:
从左往右:
小括号()、数组括号[]、成员选择 . 和 ->,双目运算符,逗号运算符
 
从右往左:
单目运算符、三目运算符、赋值类运算符
 
/*
++a:  是直接从变量 a 所在的内存地址中取值,并进行加1操作,再执行表达式剩余部分。
a++: 先把变量的值保存在一个临时寄存器里,然后再执行整个表达式,执行完之后,再把a的值自增1,再返回内存里。
 
CPU -》 寄存器 -》 缓存(L1\L2\L3) -》 来自于内存
CPU只和寄存器做数据交换,对于重复操作的数据会放在缓存里。
但是不管寄存器还是缓存,他们的数据都来自于内存。
*/
 
 
六、指针数组 和 数组指针
 
1. 指针数组:
定义形式:
int *p[n] = { 0 };
[]的优先级高于*,那么p先和[]结合,说明这是一个数组。
再和int *结合,说明这个数组里的每个元素都是一个指针,每个元素都能保存一个地址。
 
1) 使用指针数组保存多个数据的地址
int main(void)
{
int a[3] = { 10, 20, 30};
int *p[3] = { 0 };
for (int i = 0; i < 3; ++i)
p[i] = &a[i];
 
for (int i = 0; i < 3; ++i)
{
printf("%p\n", p[i]);
printf("%d\n", *p[i]);
    }
}
 
 
2) 使用指针数组保存多个字符串的首地址
#include <stdio.h>
 
int main(void)
{
char *str[5] =
{
"ISO/IEC9899:2011",
"Programming",
"Dennis Ritchie",
"c",
"bell ssss"
};
 
char *str1 = str[1]; // 取出 第2行的字符串
char *str2 = *(str + 3); // 取出 第4行的字符串
char ch1 = *(*(str + 4) + 2); //取出 第5行的字符串的第3个字符
char ch2 = (*str + 5)[7]; // 取出第1个字符串的第6个字符,并以此做数组首元素,向后遍历到第7个。
char ch3 = *str[0] + 6;  // 取出第1个字符串的第1个字符,然后把这个字符ASCII码加上6,再用%c打印出来
 
printf("str1 = %s\n",  str1); // Programming
printf("str2 = %s\n",  str2); // c
printf("ch1 = %c\n",  ch1); // l
printf("ch2 = %c\n",  ch2); // 2
printf("ch3 = %c\n",  ch3); // O (不是0)
 
return 0;
}
 
2. 数组指针(行指针)
定义形式: int *p;  int (*p)[n];   int (*p)[n][m];
()和[]的优先级相同,但是结合性是从左往右,那么p先和*相结合,说明这是一个指针。
然后再和[]结合,说明这个指针指向了一个数组。
 
示例:
1)
#include <stdio.h>
 
void func(int (*p)[3], int n);
int main(void)
{
int a[2][3] = {10, 20, 30, 40, 50, 60};
// 行:    0    1
// 列:    0   1   2   0   1   2
func(a, sizeof(a));
return 0;
}
 
void func(int p[2][3], int n);
// 明确行和列的数量
 
void func(int p[][3], int n);
//明确列的数量
 
void func(int (*p)[3], int n)  //明确列的数组指针
{
printf("%d\n", p[1][2]);
// 取出第2行的第3个元素:60
 
printf("%d\n", **p);
//p是行指针,先是取出当前行的列地址,再取出这个列地址里的值: 10
 
printf("%d\n", (*p + 1)[2]);
// 先取出列地址,然后往后移动一个int(4个字节),再以这一列的下标为起点,取出后面第2个元素的值:40
 
printf("%d\n", *(*p + 1));
// 先取出列地址,往后移动1位(1个int),再取出这个下标的值:20
 
printf("%d\n", *(p[1] + 2));
// 先取出p[1]的列下标地址,再往后移动2位(2个int),再取出这个下标的值:60
 
printf("%d\n", *(*(p + 1)));
// 先将行指针p后移一位(3个int),再取出这一行的这一列的下标地址,再根据这个下标取值:40
 
printf("%d\n", *((*p + 1) + 2));
//这个表达式相当于 *(*p + 3),先取出列下标地址,然后往后移动3位(3个int),再取值:40
 
printf("%d\n", *(*p + 1) + 2);
// 先取出列下标的地址,然后往后移动一位(1个int),再进行取值得出20,再加2,结果是:22
 
}
 
// 三维数组同理
void func(int (*p)[3][4])
{
printf("%d\n", *(*(*(p + 0) + 1 ) + 2);  //70
}
 
int main(void)
{
int a[2][3][4] =
{
{{10, 20, 30, 40}, {50, 60, 70, 80}, {90, 100, 110, 120}},
{{11, 22, 33, 44}, {55, 66, 77, 88}, {99, 101, 111, 121}}
};
 
func(a);
return 0;
}
 
总结:
1.指针数组:就是一个数组,这个数组里的每个元素都是一个指针,这个数组在内存空间中占用了n个指针的大小。
 
2.数组指针:就是一个指针,这个指针指向一个数组,这个指针在内存空间中占用一个指针的大小。
 
3.二级指针p 和 二维数组名p 的区别:
int **p; 这个是一个整型的二级指针,p是一个可变句柄/钥匙,我们可以让这个指针指向任何我们希望它指向的地方。
这个句柄不需要指定内存空间的大小。
int p[n][m]; 这个是一个整型的二维数组,p是引用了这块内存空间的句柄/钥匙,数组在定义的时候,
就被固定指向某个内存空间了,这个空间大小是 sizeof(int) * n * m,而且不可修改p的指向,
p就是一个不可变的常量,永远的都只能指向这里了。
 
如果想确定某个一维数组的值: * 、 []
如果想确定某个二维数组的值:** 、 [][] 、 *[]

本文版权归传智播客C++培训学院所有,欢迎转载,转载请注明作者出处。谢谢!
作者:传智播客C/C++培训学院
首发:http://www.itcast.cn/c/