如果对指针不是那么熟悉,我这里有几篇指针相关入门,不知道能不能帮助到你
http://t.csdn.cn/BbVwT
http://t.csdn.cn/eqBng
http://t.csdn.cn/hwNXp
看完后,检测一下这两段代码是否能透彻理解
(1)
#include<stdio.h>
int main()
{
int a=100;
int *p;
p=&a;
printf("%d",*p);
}
其输出为100
(2)
int a = 100;
int *p1 = &a; // 一级指针,指向普通变量
int **p2 = &p1; // 二级指针,指向一级指针
int ***p3 = &p2; // 三级指针,指向二级指针
- 如果一个指针变量 p1 存储的地址,是另一个普通变量 a 的地址,那么称 p1 为一级指针
- 如果一个指针变量 p2 存储的地址,是指针变量 p1 的地址,那么称 p2 为二级指针
- 如果一个指针变量 p3 存储的地址,是指针变量 p2 的地址,那么称 p3 为三级指针
- 以此类推,p2、p3等指针被称为多级指针
一.数组与指针的表示
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]
在大多情况下,指针都是指向数组的首元素,可以这么说,数组运算实际上就是指针运算。
补充:
字符串实质上是一个匿名数组,应用和数组很类似:
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" 代表匿名数组的首元素地址
a[i] = 100; 等价于 *(a+i) = 100;
所以易得,a[i]=100,也与下面几项等价
a[i] = 100;
*(a+i) = 100;
*(i+a) = 100;
i[a] = 100;
例:
用三种等价的方法表示a[0][0]
&a[0][0]
//a[0]是首元素a[0][0]的地址
a[0]
//a[0]=*(a+0)=*(a)=*a
*a
二. 数组与指针语法
数组语法剖析
- 任意的数组,不管有多复杂,其定义都由两部分组成。
- 第1部分:说明元素的类型,可以是任意的类型(除了函数)
- 第1部分:说明数组名和元素个数
int a[4]; // 第2部分:a[4]; 第1部分:int
int b[3][4]; // 第2部分:b[3]; 第1部分:int [4]
int c[2][3][4]; // 第2部分:c[2]; 第1部分:int [3][4]
int *d[6]; // 第2部分:d[6]; 第1部分:int *
int (*e[7])(int, float); // 第2部分:e[7]; 第1部分:int (*)(int, float)
指针语法剖析
- 任意的指针,不管有多复杂,其定义都由两部分组成。
- 第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);
来一个较为复杂的
int (*parr3[10])[5];//parr3是一个数组,该数组有10个元素,每个元素是一个数组指针,该数组指针指向的数组有5个元素,每个元素是int类型
分析一下
parr3[10]提出来,是一个数组,该数组有是个元素,剩下:
int(*)[5]:代表的是一个数组指针,数组指针指向的数组有5个元素,每个元素int型
例:
- 定义一个整型数 i
- 定义一个指向整型数的指针 p
- 定义一个指向整型指针的指针 k
- 定义一个有 3 个整型数的数组 a
- 定义一个有 3 个整型指针的数组 b
- 定义一个指向有 3 个整型元素的数组的指针 q
- 定义一个指向函数的指针 r,该函数有一个整型参数并返回一个整型
- 参考代码
1. int i;
2. int *p;
3. int **k;
4. int a[3];
5. int *b[3];
6. int (*q)[3];
7. int (*r)(int);
三.指针运算
指针p自增
#include <stdio.h>
int main(void)
{
int a[] = {1, 2, 3, 4};
int i, *p;
for(i=0, p=a; i<4; i++, p++)
{
printf("%d %d\n", a[i], *p);
}
return 0;
}
注:这里p=a,表示让指针指向数组a的首元素1,再p++,使指针p不断自增
得到输出结果为
1 1
2 2
3 3
4 4
*p和*(p+1)
#include<stdio.h>
int main()
{
int *p;
int a[2][2] = {{1, 0}, {2, 3}};
p= a[0];
printf("%d, %d", *p, *(p+1));
return 0;
}
p指向数组的首元素a[0][0],所以*p=a[0][0]的值,*(p+1)=p[0][1]
所以输出为:1,0
重要知识点,先来看代码
将字符串中的小写字母转换为大写字母
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
void upper_case(char str[])
{
int step = 'a' - 'A';
for(int i = 0; i<sizeof(str)/sizeof(str[0]); i++)
{
if(islower(str[i]))
str[i] -= step;
}
}
int main(void)
{
char str[] = "abcdefghijklnmopqrstuvwxyz";
printf("原数组:%s\n", str);
upper_case(str);
printf("转换后:%s\n", str);
}
这段代码有一个错误,在函数upper_case
中,sizeof(str)/sizeof(str[0])
的结果并不是数组长度,而是指针的大小。
这是因为数组在除了定义和sizeof语句之外,均会被视为指向其首元素的指针,因此在上述代码中, upper_case(str) 中的str是一个指针,而非数组,等价于:
upper_case(&str[0]);
所以str永远都是指针,而非数组,所以sizeof(str)无法计算得数组大小
在这里,可以将数组长度作为额外的参数传递给函数upper_case
,或者使用一个特殊的字符(如'\0')作为数组的结束标志,然后在循环中检查该标志来确定数组的长度。
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
void upper_case(char str[])
{
int step = 'a' - 'A';
for(int i = 0; str[i] != '\0'; i++)
{
if(islower(str[i]))
str[i] -= step;
}
}
int main(void)
{
char str[] = "abcdefghijklnmopqrstuvwxyz";
printf("原数组:%s\n", str);
upper_case(str);
printf("转换后:%s\n", str);
return 0;
}
四.结构体中指针的应用
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]
五.进阶练习
(1)编写一个程序,测试当前平台的字节序
#include <stdio.h>
int main(void)
{
// 定义一个4字节的整型数据
int a = 0x12345678;
// 定义一个char型指针指向最低地址
char *p = &a;
// 将最低字节数据打印出来
// 如果是0x78,那就代表最低地址存储了低有效位,是小端序
// 如果是0x12,那就代表最低地址存储了高有效位,是大端序
printf("%#x\n", *p);
}
(2)(数组下标运算、指针运算、内存操作)
注:这里的sizeof(long)是四个字节
#include <stdio.h>
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],即4。
其次,(long)a+1 是一个纯整数运算(因为a被强转为long了),因此 long型指针p2 就指向了long型数据 a[0] 的第二个字节
最后打印 *p2 时,由于 p2 是一个 long 型指针,系统会从 a[0] 的第二个字节开始,取出 sizeof(long) 个字节出来作为 long 型数据来解释,因此最后输出的结果是 a[0] 的高位三字节和 a[1] 低位一字节的数据
a[0]的第二个字节开始,往后数四个字节,就是a[0] 的高位三字节和 a[1] 低位一字节的数据
(3)声明一个二维 int 型数组 a,再声明另一个一维数组指针数组 b,使该数组 b 的每一个指针分别指向二维数组 a 中的每一个元素(即每一个一维数组),然后利用数组 b 计算数组 a 的和。
注:“指针数组”指的是装了很多指针的数组,“数组指针”指的是指向数组的指针,“数组指针数组”指的是装了很多数组指针的数组。
#include <stdio.h>
int main(void)
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*b[2])[3];
1. *b[2]//数组b有两个元素,每个元素都是一个指针,与int (*b)[2]进行区别,这里是包含两个整形元素的指针
2. int (*b[2])[3]//该数组是包含两个指针的数组,每个指针指向3个int类型元素
b[0] = &a[0];//第一个指针指向a[0]的地址
b[1] = &a[1];//第二个指针指向a[1]的地址
int i, j, sum=0;//初始化sum
for(i=0; i<2; ++i)
{
for(j=0; j<3; ++j)
{
sum += (*b[i])[j];//作和
}
}
printf("sum: %d\n", sum);
return 0;
}
(4)编写一个程序,去掉给定字符串中重复的字符。
#include <stdio.h>
#include <string.h>
void strip(char *str)
{
if(str == NULL)
return;
int i, j;
char *p = str;
// 将所有重复的字符标记为-1
for(i=1; str[i] != '\0'; i++)
{
for(j=0; j<i; j++)
{
if(p[j] == str[i])
{
str[i] = -1;
break;
}
}
}
// 找到第一个-1的位置 redun
// 找到紧随 redun 之后的正常字符的位置 common
int redun = 0, common = 0;
for(i=1; str[i] != '\0'; i++)
{
if(str[i] != REDUNDANT)
continue;
redun = i;
while(str[i] == REDUNDANT)
i++;
common = i;
break;
}
// 若没有重复的字符,则返回
if(redun == 0)
return;
// 将所有正常字符往前移
while(str[common] != '\0')
{
if(str[common] == -1)
{
common++;
continue;
}
str[redun++] = str[common];
str[common++] = -1;
}
str[redun] = '\0';
}
int main(void)
{
// 输入待处理字符串
char buf[64];
fgets(buf, 64, stdin);
//对于fgets的应用可以看
//http://t.csdn.cn/mDMBb
strip(buf);//删除buf字符串
printf("%s", buf);
return 0;
}
strip示例
整理写作不易,其中有错误的地方,请大佬们不吝赐教!!💓💓