C语言指针概述
指针(Pointer)是编程语言的一个对象,它直接对地址进行操作。在正常的PC端软件开发中,指针可以更方便的表示各种数据结构,更高效的传递参数,提高编程效率。在嵌入式开发中指针除了上述优点,更为强大的一点是指针可以直接对地址进行操作,这也就意味着在嵌入式开发中我们可以使用指针直接对寄存器进行操作,减少了汇编语言的使用,方便开发,降低难度。
指针的定义
类型* 指针变量名;//定义了一个指向某数据类型的指针变量
int* a = NULL;//a是一个指向int型数据的指针,a存的是地址,*a是该地址内存放的int型变量
指针的初始化
指针在使用前必须进行初始化
指针的初始化过程是为指针分配地址的过程,而不是直接赋值,通常不能直接将常数赋值给指针。
数组指针那块(p)[5]包含五个元素,这五个元素都是指向数组的指针,而代码中只对p[0]进行了初始化,p[1]到[4]均未进行初始化,其指向系统随机赋予的内存地址,也就是野指针。若要访问a[1]-[4]的内容应使用(p[0]+1)、(p[0]+2)、(p[0]+3)、*(p[0]+4)。
在Linux中创建例程实验:
mkdir pointer
mkdir +文件夹名称——创建文件夹
cd pointer
cd + 文件路径——进入该文件夹
vim pointer1.c
vim + 文件名——创建文件并进入编辑
输入下列代码:
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int a = 3;
6 int* b = NULL;//初始化为空指针
7 int* c = &a;//将b的地址赋给指针c,c指向b的地址,*c为该地
址存储的数据3
8 int* d;
9 b = &a;
10 d = &a;//此时d也指向b的地址,也就是说此时的c = d
11
12 printf("The address of a is:%p\r\n",&a);
13 printf("The address of b is:%p\r\n",b);
14 printf("The address of c is:%p\r\n",c);
15 printf("The address of d is:%p\r\n",d);
16 printf("\r\n");
17 printf("The number of a is:%d\r\n", a);
18 printf("The number of b is:%d\r\n",*b);
19 printf("The number of c is:%d\r\n",*c);
20 printf("The number of d is:%d\r\n",*d);
21
22 printf("\r\n");
23 *b = 6; //通过指针直接改变内存空间中存储的数据
24 printf("The address of a is:%p\r\n",&a);
25 printf("The address of b is:%p\r\n",b);
26 printf("The address of c is:%p\r\n",c);
27 printf("The address of d is:%p\r\n",d);
28 printf("\r\n");
29 printf("The number of a is:%d\r\n", a);
30 printf("The number of b is:%d\r\n",*b);
31 printf("The number of c is:%d\r\n",*c);
32 printf("The number of d is:%d\r\n",*d);
33
34 return 0;
35 }
编译运行:
gcc -o pointer1 pointer1.c
gcc -o + 目标文件名 + 源文件名——将源文件进行预处理、汇编、编译、链接后生成目标文件,目标文件为可执行文件
ls——调出当前文件夹的目录,确认是否生成pointer1,非必须步骤
./pointer1
./ + 文件名——运行该文件
运行结果:
The address of a is:0xbf89f78c
The address of b is:0xbf89f78c
The address of c is:0xbf89f78c
The address of d is:0xbf89f78c
The number of a is:3
The number of b is:3
The number of c is:3
The number of d is:3
The address of a is:0xbf89f78c
The address of b is:0xbf89f78c
The address of c is:0xbf89f78c
The address of d is:0xbf89f78c
The number of a is:6
The number of b is:6
The number of c is:6
The number of d is:6
空指针与未初始化指针
空指针是指初始化为NULL的指针,它不指向任何一个地址。
未初始化指针是只进行了定义而没有初始化的指针,属于野指针。未初始化的指针会被随机赋予一个地址,在PC端运行时如果这个地址指向某个正在运行的程序,一旦对这个指针进行修改就极有可能造成改程序的崩溃,而野指针在嵌入式系统中野指针通常会造成更更大危害,大概率会造成整个程序的崩溃,而且野指针在大型单片机工程中很难被发现,因为野指针是不会报错的,不会由error也不会有warning,只能一步一步排查错误,所以指针类型的变量一定要在定义后进行初始化,或赋NULL使其成为空指针。
数组指针和指针数组
数组指针:指向数组的指针
在Linux中创建例程实验:
mkdir pointer
mkdir +文件夹名称——创建文件夹
cd pointer
cd + 文件路径——进入该文件夹
vim pointer2.c
vim + 文件名——创建文件并进入编辑
输入下列代码:
#include "stdio.h"
int main()
{
//一维数组
int array[5] = { 1, 2, 3, 4, 5 };
//步长为5的数组指针,即数组里有5个元素
int (*pointer)[5];
//把数组a的地址赋给p,则p为数组a的地址,则*p表示数组a本身
p = &a;
//%p输出地址, %d输出十进制
//\n回车
//在C中,在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址,它的类型取决于数组元素的类型。
printf("%p\n", a); //输出数组名,一般用数组的首元素地址来标识一个数组,则输出数组首元素地址
printf("%p\n", p); //根据上面,p为数组a的地址,输出数组a的地址
printf("%p\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组
printf("%p\n", &a[0]); //a[0]的地址
printf("%p\n", &a[1]); //a[1]的地址
printf("%p\n", p[0]); //数组首元素的地址
printf("%d\n", **p); //*p为数组a本身,即为数组a首元素地址,则*(*p)为值,当*p为数组首元素地址时,**p表示首元素的值1
printf("%d\n", *p[0]); //根据优先级,p[0] 表示首元素地址,则*p[0]表示首元素本身,即首元素的值1
printf("%d\n", *p[1]); //为一个绝对值很大的负数,不表示a[1]...表示什么我还不知道
}
编译运行:
gcc -o pointer2 pointer2.c
gcc -o + 目标文件名 + 源文件名——将源文件进行预处理、汇编、编译、链接后生成目标文件,目标文件为可执行文件
ls——调出当前文件夹的目录,确认是否生成pointer2,非必须步骤
./pointer2
./ + 文件名——运行该文件
运行结果:
The address of a is: 0xbfe344bc
The address of array[0] is:0xbfe344bc
The number of a is: 1
The number of array[0] is: 1
The address of b is: 0xbfe344c0
The address of array[1] is:0xbfe344c0
The number of b is: 2
The number of array[1] is: 2
指针数组:装着指针的数组
定义:数据类型 *数组名 [常数表达式];
例:int *a[2];//定义了一个包含两个元素的数组,这两个元素是指向int数据类型的指针
在Linux中创建例程实验:
mkdir pointer
mkdir +文件夹名称——创建文件夹
cd pointer
cd + 文件路径——进入该文件夹
vim pointer3.c
vim + 文件名——创建文件并进入编辑
输入下列代码:
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int *array[2];//数组p中还有两者指向int类型的指针元素
6 int a = 1;
7 int b = 2;
8 array[0] = &a;
9 array[1] = &b;
10
11 printf("The address of a is: %p\r\n",&a);//%p输出地址
12 printf("The address of array[0] is:%p\r\n",array[0]);
13 printf("The number of a is: %d\r\n",a);
14 printf("The number of array[0] is: %d\r\n",*array[0]);
15 printf("\r\n");
16 printf("The address of b is: %p\r\n",&b);
17 printf("The address of array[1] is:%p\r\n",array[1]);
18 printf("The number of b is: %d\r\n",b);
19 printf("The number of array[1] is: %d\r\n",*array[1]);
20
21 return 0;
22 }
编译运行:
gcc -o pointer3 pointer3.c
gcc -o + 目标文件名 + 源文件名——将源文件进行预处理、汇编、编译、链接后生成目标文件,目标文件为可执行文件
./pointer3
./ + 文件名——运行该文件
运行结果:
The address of a is: 0xbfe344bc
The address of array[0] is:0xbfe344bc
The number of a is: 1
The number of array[0] is: 1
The address of b is: 0xbfe344c0
The address of array[1] is:0xbfe344c0
The number of b is: 2
The number of array[1] is: 2
结构体指针
结构体指针
/*定义一个结构体str*/
struct str
{
int a = 1;
int* b;
char c[];
};
/*定义一个指向结构体的指针*/
struct str *s;
/*此时结构体指针有两种初始化方法*/
/*一、新建一个结构体变量,将该变量的地址赋予结构体指针*/
struct str s1;
s = &s1;
/*
* 二、使用内存分配函数给结构体指针分配内存(分配地址)
* 此方法赋值会将结构体中已经赋值的变量赋随机值,需重赋值
*/
s = (struct str*)malloc(sizeof(struct str));
内存分配函数给结构体指针分配一块内存,结构体指针s指向这块内存的首地址,这块内存保存变量a、指针变量b、字符型数组c,这三个变量的内存地址是连续的,由于变量a是结构体内的第一个变量,因此变量a的地址等于结构体指针s的地址。
#include <iostream>
#include <stdio.h>
struct str
{
int a = 1;
int* b;
char c[];
};
int main()
{
/*定义一个指向结构体的指针*/
struct str *s;
char str[] = "abcd";
/*此时结构体指针有两种初始化方法*/
/*一、新建一个结构体变量,将该变量的地址赋予结构体指针*/
//struct str s1;
//s = &s1;
/*二、使用内存分配函数给结构体指针分配内存(分配地址)*/
s = (struct str*)malloc(sizeof(struct str));
int d = 2;
s->a = 1;
s->b = &d;
*s->c = *str;
std::cout << "结构体指针s的地址 = " << s << "\r\n";
std::cout << "变量a的地址 = " << &s->a << "\r\n" << "a = " << s->a << "\r\n";
std::cout << "指针变量b地址 = " << &s->b << "\r\n" << "指针变量指向的地址 = " << s->b << "\r\n" << "b = " << *s->b << "\r\n";
std::cout << "数组c指向的地址 = " << &s->c << "\r\n" << "数组c中存储的值 = " << s->c[0] << "\r\n";
system("pause");
}
脑子已经乱套了,我去理理思绪
结构体内部的指针变量
结构体指针指向结构体内部的指针变量