C语言指针学习
前言
1、指针变量是用来存储数据所在地址的。
2、变量名、函数名、字符串名和数组名在本质上是一样的,它们都是地址的助记符。
3、* 在 char *p; 中表示定义指针变量,在 *p=5; 中表示指针p指向地址中的值;“&”运算符表示取变量地址。
1. 一般数据指针
int a=5, *p;
p=&a; //表示将变量a数据存储的地址赋给指针变量p
推广:
a=*p; // 此处 * 表示解引用,即取存储在地址p 中的数值
即有:a=5,*p=5;
例:
#include <stdio.h>
void main()
{
int a=5,*p;
p=&a;
printf("a=%d, *p=%d\n",a,*p);//输出数值
printf("%x, %x\n",&a,p);//输出地址
}
运行结果:
2. 一维数组指针
int a[3]; //定义了一个3个元素的一维数组,数组名字为a,数组名代表数组首地址
int *p; // 此处 * 表示定义指针变量
p=a; //p=&a[0] // (&表示取地址运算符)表示将数组首地址赋值给p
推广:
p+i=a+i=&a[i]=&p[i] //表示数组的第i个元素的地址
*(p+i)= * (a+i)=a[i]=p[i]//表示第i个元素的地址中的值 此处 * 表示索引存储地址中的数值
例:
#include <stdio.h>
void main()
{
int a[3],*p;
int i;
a[0]=2;
a[1]=4;
a[2]=6;
p=a;
printf("输出数据所在地址:\n");
for(i=0;i<3;i++)
printf("第 %d 个数据的存储地址:%x %x %x %x\n",i,p+i,a+i,&a[i],&p[i]);
printf("输出相应地址中的数据:\n");
for(i=0;i<3;i++)
printf("第 %d 个数据的存储地址中数值:%d %d %d %d\n",i,*(p+i),*(a+i),*(&a[i]),*(&p[i]));
//&a[i],&p[i]表示取数组中第i个元素的地址
//*(地址)表示索引对应地址中的数值
}
运行结果:
3. 二维数组指针
float a[3][3];//对于多维数组,数组名同样代表数组首地址
int *p;
p=a[0]; //p=a;表示将数组第0行首地址赋值给p
推广:
p+i;
//二维数组的元素都按照内存顺序排列;
//表示从数组a起始地址开始第i个元素的地址,p+6表示访问第7个元素的地址//
p=a; //表示数组的起始地址
a+i= a[i] =(a+i)=&a[i]=&a[i][0]//这几个地址是等价的
a[i]+j=&a[i][j]=(a+i)+j//表示第i行第j列的元素地址
(a[i]+j)=a[i][j]=(*(a+i)+j)//表示第i行第j列中的元素
4. 指针数组、字符串指针数组
#include "stdio.h"
#include "string.h"
int main(void)
{
int a1[] = { 1,2,3 }; int a2[] = { 4,5,6 }; int a3[] = { 7,8,9 };
int* num[] = { a1,a2,a3};
for (int i = 0; i < 3; i++)
{
printf("%x %x ", num + i, *(num + i));
// num + i 数组对应下标占用的内存地址;*(num + i) 装入的数组元素首地址
for (int j = 0; j < 3; j++)
{
printf("%x %d ", *(num + i)+j, *(*(num + i)+j));
// *(num + i) 数组中装有的地址;*(num + i)+j 数组中装有的地址+偏移量
// *(num + i) + j 解引用(数组中装有的地址 + 偏移量)元素
}
printf("\n");
}
return 0;
}
运行结果:
第一个红框为数组num每个下标元素占用的内存地址;第二个红框为num数组中装有数组的首地址;
#include "stdio.h"
#include "string.h"
int main(void)
{
char str[50] = {0};
char* substr[11] = { "AB","ABC","ABCD","ABCDE","ABCDEF","ABCDEFG","ABCDEFGH",
"ABCDEFGHI","ABCDEFGHIJ","ABCDEFGHIJK","ABCDEFGHIJKL" };
//char型指针数组中,装入的数据为每个字符串的首地址
for (int i = 0; i < 11; i++)
{
sprintf(str,"The string of length %02d-%0.2d is: %s.", strlen(*(substr + i)), strlen(*(substr + i)) ,*(substr +i));
// substr +i 数组对应下标占用的内存地址;*(substr +i) 解引用得到每个字符串的首地址(从对应下标地址中取出对应数据)
// sprintf 函数为 "stdio.h" 中 将格式化的数据写入字符串函数
// %02d、%0.2d、%.2d 功能为将数据从左边补0对齐为2位
printf("%s\n", str);
}
return 0;
}
程序运行结果:
3. 函数指针
后面再继续补充。
扩展:
要从地址 addr,读取一个半字(半字为 16 位,字为 32 位),可以通过如下的语句读取:
data= * (vu16 *)addr;//vu16为 volatile unsigned short int
将 addr 强制转换为 vu16 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。
类似的,将上面的 vu16 改为 vu8,即可读取指定地址的一个字节。
使用指针的2大问题:
1.未分配内存就使用
char *p;
p[1]=2;//编程时不报错,运行程序会出错
2.越界使用
char *p={1,2,3,4,5};
p[10]=12;//可能导致修改其他变量的值,还可能地址超出物理内存地址,造成编程时hardfault 报错。