嵌入式(Linux)系列文章目录
第一章 C语言の第一节 必备Linux命令和C语言基础
第一章 C语言の第二节 数据类型、常量、变量及运算符
第一章 C语言の第三节 输入输出专题
第一章 C语言の第四节 控制语句
第一章 C语言の第五节 数组和字符串
第一章 C语言の第六节 指针
文章目录
前言
本文章采用VMware Workstation Pro进行嵌入式(Linux)系列学习
从零开始,重新学习(嵌入式相关内容)并记录知识点(涉及基础c语言(数据结构)、linux编程、shell脚本、文件io、网络编程)等),同时也是为了方便自身回顾,如有不对的地方请大佬多多指教,谢谢!->欢迎评论区留言
第一章 C语言
第六节 指针
1.指针的基本用法
- C程序设计中使用指针的优点
- 使程序简洁、紧凑、高效
- 有效地表示复杂的数据结构
- 动态分配内存
- 得到多于一个的函数返回值
1.1 地址和变量
- 地址
- 在计算机内存中,每一个字节单元(1Byte = 8Bit),都有一个编号,称为地址
- 变量
- 编译或函数调用时为其分配内存单元
- 是对程序中数据存储空间的抽象
1.2 指针
- 在C语言中,内存单元的地址称为指针,专门用来存放地址的变量,称为指针变量
- 在不影响理解的情况中,有时对地址、指针和指针变量不区分,统称指针
1.2.1 指针变量的说明
- 格式
<存储类型> <数据类型> *<指针变量名>;
- 指针的存储类型是指针变量本身的存储类型
- 指针说明时指定的数据类型不是指针变量本身的数据类型,而是指针目标的数据类型(简称为指针的数据类型)
1.2.2 指针的初始化(赋初值)
- 格式
<存储类型> <数据类型> *<指针变量名> = <地址量>;
1.2.3 指针指向的内存区域中的数据称为指针的目标
- 如果它指向的区域是程序中的一个变量的内存空间,则这个变量称为指针的目标变量(简称为指针的目标)
1.2.4 引入指针时不同的表示方法代表的不同意义
- 设p为一个指针,则:
- p:指针变量,它的内容是地址量
- *p:指针所指向的对象,它的内容是数据
- &p:指针变量所占用的存储区域的地址,是个常量
1.2.5 指针的赋值运算
- 即通过赋值运算符向指针变量送一个地址值
注:向一个指针变量赋值时,送的值必须是地址常量或指针变量,不能是普通的整数(除了0)
- 指针赋值运算
- 把一个普通变量的地址赋给一个具有相同数据类型的指针
- 把一个已有地址值的指针变量赋给具有相同数据类型的另一个指针变量
- 把一个数组的地址赋给具有相同数据类型的指针
2.指针的运算
- 指针运算是以指针变量所存放的地址量作为运算量而进行的运算
- 指针运算的实质就是地址的计算
- 指针运算的种类是有限的,它只能进行赋值运算、算术运算和关系运算
2.1 指针的算术运算
运算符 | 计算形式 | 意义 |
---|---|---|
+ | px+n | 指针向地址大的方向移动n个数据 |
- | px-n | 指针向地址小的方向移动n个数据 |
++ | px++ | 指针向地址大的方向移动1个数据 |
– | px– | 指针向地址小的方向移动1个数据 |
- | px-py | 两个指针之间相隔数据元素的个数 |
- 指针加减一个n的运算:px+n、px-n
- 注意
- 不同数据类型的两个指针实行加减整数运算是无意义的
- px + n表示的实际位置的地址量是:(px) + sizeof(px的类型) * n
- px - n表示的实际位置的地址量是:(px) - sizeof(px的类型) * n
- 两指针相减运算(结果为实际相隔的数据个数,而不是地址运算)
- px - py运算的结果是两指针指向的地址位置之间相隔数据的个数,因此两指针相减不是两指针持有的地址值相减的结果
- 两指针相减的结果值不是地址量,而是一个整数值,表示两指针之间相隔数据的个数
- 指针加一、减一运算:px++、++px、px- -、- -px
2.2 指针的关系运算
运算符 | 计算形式 | 意义 |
---|---|---|
> | 大于 | px > py |
< | 小于 | px < py |
>= | 大于等于 | px >= py |
<= | 小于等于 | px <= py |
!= | 不等于 | px != py |
== | 等于 | px == py |
- 指针关系运算
- 两指针之间的关系运算表示他们指向的地址位置之间的关系(指向地址大的指针大于指向地址小的指针)
- 指针与一般整数变量之间的关系运算没有意义,但可以与零进行等于或不等于的关系运算,判断指针是否为空(=NULL,即0(nil))
gcc -e xxx.c -o xxx.i
int *p = NULL; 实际为 int *p = ((void *)0);
- 程序示例
int main()
{
int a[] = {9,8,0,8,2,0};
int y, *p = &a[1];
y = (*--p)++; // 先赋值再((*--p)= a[0])自加
printf("%d",y);
printf("%d",a[0]);
}
- 结果
9 10
- 思考
- 指针运算的本质是什么?
答:地址的运算
- 指针加1,移动多少字节?
答:移动N个目标数据(int 4个,double 8个)
3.指针与数组
- 在C语言中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址
- 一维数组的数组名为一位数组的指针(起始地址)
- 设指针变量p的地址值等于数组指针x(即指针变量p指向数组的首元素),则:
- x[i]、(p+i)、(x+i)和p[i]具有完全相同的功能:访问数组第i+1个数组元素
a[0],a[1],a[2],...
下标法地址 元素
a a[0]、*a
a+2 a[2]、*(a+2)
指针法地址 元素
p *p、p[0]
p+2 *(p+2)、p[2]
注:
指针变量和数组在访问数组中元素时,一定条件下其使用方法具有相同的形式,因为指针变量和数组名都是地址量
但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量,而数组的指针是地址常量
- 程序示例
- 整形数组中n个数按反序存放
#include<stdio.h>
int main(int argc, char *argv[])
{
int a[] = {1,2,3,4,5};
int *p, *q, n, t;
n = sizeof(a) / sizeof(int);
p = a;
q = &a[n - 1];
while (p < q)
{
t = *p;
*p = *q;
*q = t;
p++;
q--;
}
for (t = 0; t < n; t++)
{
printf("%d", a[t]);
}
puts("");
}
4.指针与二维数组
- 多维数组就是具有两个或两个以上下标的数组
- 在C语言中,二维数组的元素连续存储,按行优先存
- 程序示例
#include<stdio.h>
int main(int argc, char *argv[])
{
int a[] = {{1,2},{3,4},{5,6}};
int *p, n, t;
n = sizeof(a) / sizeof(int);
p = a[0]; // &a[0][0];
printf("%d %d\n", p, p+1);
printf("%d %d\n", a, a+1);
for (t = 0; t < n; t++)
{
printf("%d", *(p+t));
}
puts("");
}
- 结果
0xfffffff0 0xfffffff4
0xfffffff0 0xfffffff8
1 2 3 4 5 6
- 可把二维数组看作由多个一维数组组成
- 二维数组名代表数组的起始地址,数组名加1,是移动一行元素。因此二维数组名常被称为行地址
a -> a[0] a[0][0] *(a+0)
a[0]+1 a[0][0]
a[0][0]
a+1 -> a[1] a[1][0] *(a+1) <=> a[1]
a[1]+1 a[1][0]
a[1][0]
a+2 -> a[2] a[2][0] *(a+2)
a[2][0]
a[2][0]
星号(*)作用:改变了指针的性质
- 行指针(数组指针)
- 存储行地址的指针变量,叫做行指针变量。格式如下
<存储类型> <数据类型> (*<指针变量名>)[表达式];
- 方括号中的常量表达式表示指针加1,移动几个数据
- 当用行指针操作二维数组时,表达式一般写成1行的元素个数,即列数
- 思考
- 二维数组名有什么特点?
答:地址常量;二维数组名(行地址)+1 表示移动一行
5.字符指针与字符串
- C语言通过使用字符数组来处理字符串
- 通常,我们把char数据类型的指针变量称为字符指针变量。字符指针变量与字符数组有着密切关系,他也被用来处理字符串
- 初始化字符指针是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针中
- 在C编程中,当一个字符指针指向一个字符串常量(静态存储区:程序结束时才释放内存,包括全局变量、static修饰的局部变量、字符串常量)时,不能修改指针指向的对象的值
- 思考
- 字符指针操作字符串有什么注意事项?
答:字符指针指向字符串常量时无法更改其内容
6.指针数组
- 由若干个具有相同存储类型和数据类型的指针变量构成的集合
- 指针数组的一般格式
<存储类型><数据类型> *<指针数组名>[<大小>];
- 指针数组名表示该指针数组的起始地址
- 思考
- 如何计算指针数组占用的内存空间?
答:任何指针占4个字节*数组元素个数
- 指针数组名加1,移动多少字节?
答:指针数组的任何元素都为指针,则移动一个指针的字节,即4个字节
- 指针数组名相当于什么样的指针?
答:一级指针取地址,即二级指针
7.多级指针
7.1 多级指针的定义
- 把一个指向指针变量的指针变量,称为多级指针变量
- 对于指向处理数据的指针变量称为一级指针变量,简称一级指针
- 而把指向一级指针变量的指针变量称为二级指针变量简称二级指针
- 二级指针变量的格式
<存储类型><数据类型> **<指针名>;
7.2 多级指针的运算
- 指针变量加1,是向地址大的方向移动一个目标数据。
类似的道理,多级指针运算也是以其目标变量为单位进行偏移 - 比如,int * * p;p+1移动一个int * 变量所占的内存空间。再比如int***p,p+1移动一个int * *所占的内存空间
7.3 多级指针和指针数组
- 指针数组也可以用另外一个指针来处理
- 代码示例
#include <stdio.h>
int main(int argc, char *argv[])
{
//char *s1 m"apple";
//char *s2 ="pear";
char *s[] = ["apple","pear","potato"};
char **p;
int i, n;
i = 0;
n = sizeof(s) / sizeof(char *);
p = &s[0]; //p = s;
while (i < n)
{
printf(%s %s\n",s[i], *(p + i));
i++;
}
}
- 思考
- 多级指针在内存中占几个字节?
答:本质还是指针(32位系统占4字节)
- 多级指针加1,移动多少字节?
答:指针数组的任何元素都为指针,则移动一个指针的字节,即4个字节
8.void指针和const修饰符
8.1 void指针
- void指针是一种不确定数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量
- 格式
void *<指针变量名称>;
- 对于void指针,在没有强制类型转换之前,不能进行任何指针的算术运算
- 代码示例
#include <stdio.h>
int main(int argc, char *argv[])
{
int m = 10;
double n = 3.14;
void *p,*q;
p = &m; //(void *)&m;
printf("%d %d\n",m,*(int *)p);
g = &n; //(void *)&n;
printf("%.2lf %.2lf\n", n,*(double *)q);
return 0;
}
8.2 const修饰符
8.2.1 常量化变量的值
- 格式
const <数据类型> 变量名[=<表达式>];
- 常量化变量是为了使得变量的值不能修改
- 变量有const修饰时,若想用指针间接访问变量,指针也要有const修饰。const放在指针声明的什么位置呢 ?
8.2.2 常量化指针目标表达式
- 格式
const <数据类型> *<指针变量名称>[=<指针运算表达式>];
- 常量化指针目标是限制通过指针改变其目标的数值 ,但<指针变量>存储的地址值可以修改
8.2.3 常量化指针变量
- 格式
<数据类型> *const <指针变量名称>[=<指针运算表达式>];
- 使得<指针变量>存储的地址值不能修改。但可以通过*<指针变量名称>可以修改指针所指向变量的数值
- 思考
- void指针有什么作用?
答:可以使指针功能更加通用
- main函数是否可以带参数(涉及到const指针)?
答:
- 代码示例
#include <stdio.h>
int main(int argc(参数个数), const char *argv[](输入参数至字符串)) //const char *argv[] = {"/a.out", "192.168.1.7", "9999"};
{
int i;
printf("argc = %d\n",argc);
for (i = 0; i < argc; i++)
{
printf(%s\n", argv[i]);
}
return 0;
}
- 结果
linux@ubuntu:-/cbase/void const$ ./a.out 192.168.1.7 9999
argc=3
./a.out
192.168.1.7
9999
linuxaubuntu:-/cbase/void const$
总结
本章为嵌入式学习系列文章 第一章:C语言の第六节 指针