C语言之指针变量
菜单导航
一、指针变量
/*
1、定义
普通变量: 数据类型 变量名称
指针变量: 数据类型 * 变量名称;
2、指针变量是什么类型,那么将来就只能保存什么类型变量的地址,
例如 指针变量是int类型, 那么将来就只能保存int类型变量的地址
3、* : 标示这是一个指针变量,代表访问指针变量指向的那一块存储空间
4、指针变量只能存储地址
*/
int num = ;
char c = 'a';
float f = .f;
double d = 22.5; printf("num地址:%p, c地址:%p, f地址:%p, d地址:%p \n", &num, &c, &f, &d);
printf("num字节: %zu, c字节:%zu, f字节:%zu, d字节:%zu \n", sizeof(num), sizeof(c), sizeof(f), sizeof(d)); int *pNum = #
char *pC = &c;
float *pF = &f;
double *pD = &d;
printf("pNum地址:%p, pC地址:%p, pF地址:%p, pD地址:%p \n", &pNum, &pC, &pF, &pD);
printf("pNum字节: %zu, pC字节:%zu, pF字节:%zu, pD字节:%zu \n", sizeof(pNum), sizeof(pC), sizeof(pF), sizeof(pD)); printf("改之前的值-->num: %d, c: %c, f: %f, d: %lf \n", num, c, f, d);
*pNum = ;
*pC = 'c';
*pF = 222.5f;
*pD = ;
printf("改之后的值-->num: %d, c: %c, f: %f, d: %lf \n", num, c, f, d); /** 打印日志 //2, 1, 0, f, e, d, c, b, a, 9, 8, 7
num地址:0x7fff5fbff758, c地址:0x7fff5fbff757, f地址:0x7fff5fbff750, d地址:0x7fff5fbff748
num字节: 4, c字节:1, f字节:4, d字节:8
pNum地址:0x7fff5fbff740, pC地址:0x7fff5fbff738, pF地址:0x7fff5fbff730, pD地址:0x7fff5fbff728
pNum字节: 8, pC字节:8, pF字节:8, pD字节:8
改之前的值-->num: 10, c: a, f: 12.000000, d: 22.500000
改之后的值-->num: 111, c: c, f: 222.500000, d: 333.000000
*/
指针变量的应用练习1:交换两个变量的值
#include <stdio.h> /** 交换变量值:地址传递 */
void swop2(int *v1,int *v2)
{
int temp = *v1;
*v1 = *v2;
*v2 = temp;
} /** 交换变量值:值传递 */
void swop(int v1,int v2)
{
int temp = v1;
v1 = v2;
v2 = temp; } int main()
{
int a = , b = ; //定义两个int类型变量 printf("交换前a: %i, b: %i \n", a, b); //要求交换变量a和b的值
//第一种尝试:传入变量a和b的变量名,在函数内部交换两个变量的值
swop(a, b);
printf("。。。传入变量名调用函数交换变量值: \n");
printf("交换后a: %d, b: %d \n", a, b); //第二种尝试: 传入变量a和b的地址,在函数内部交换两个变量的值
swop2(&a, &b);
printf("。。。传入变量地址调用函数交换变量值:\n");
printf("交换后a: %d, b: %d\n", a, b); /** 打印结果:
交换前a: 20, b: 30
。。。传入变量名调用函数交换变量值:
交换后a: 20, b: 30
。。。传入变量地址调用函数交换变量值:
交换后a: 30, b: 20
*/ printf("\n");
return ;
}
指针变量的应用练习2:一个函数传入3个变量,求返回三个变量的和和平均值
#include <stdio.h> /** 传入3个int变量,求3个变量之和 与 平均值 */
int getSum(int v1, int v2, int v3, int *ave){
int sum = v1 + v2 + v3;
*ave = sum/;
return sum;
} int main()
{
int v1 = , v2 = , v3 = ;
int ave; //存储平均值
int sum = getSum(v1, v2, v3, &ave);
printf("v1: %d, v2: %d, v3: %d, 和sum: %d, 平均值ave: %d \n", v1, v2, v3, sum ,ave);
//打印结果: v1: 18, v2: 998, v3: 188, 和sum: 1204, 平均值ave: 401 printf("\n");
return ;
}
指针变量注意点:
//1、指针只能保存地址
int num = ; //int *p = num;
//printf("p: %i \n", *p); //这样运行会挂 //2、同一个变量可以有多个指针指向它
int *p = #
int *p2 = #
printf("num: %d, *p: %i, *p2: %d \n", num, *p, *p2);
*p2 = ;
printf("改后num: %d, *p: %i, *p2: %d \n", num, *p, *p2);
/** 打印日志:
num: 10, *p: 10, *p2: 10
改后num: 88, *p: 88, *p2: 88
*/ //3、指针的指向可以修改
int num2 = ;
p2 = &num2;
*p2 = ;
printf("num: %d, *p: %d, *p2: %d, num2: %d \n", num, *p, *p2, num2);
//打印日志:num: 88, *p: 88, *p2: 99, num2: 99 //4、不要访问野指针:野指针值没有赋值的指针
int *p3 = NULL;
//*p3 = 12; //这样运行会挂
// 5、指针变量和变量类型要一致,即int类型指针只能存储int类型的变量的地址
// 因为当我们利用指针去取值的时候,系统会自动根据指针的类型来确定应该取对少个字节的值.
int intValue = ;
char *p = &intValue;
printf("*p: %i \n", *p);
/**
打印日志:*p: 6
为什么?
intValue = 518 的二进制为:
0000 0000 0000 0000 0000 0010 0000 0110 指针变量p是char类型,只会存储char类型的地址,即一个字节的地址,
所以指针变量p只存储了intValue的最小字节地址,即0000 0110这个字节的地址,转成十进制为:6
*/
多级指针测试:
//一级指针
char ch = 'a';
char *p = &ch; *p = 'b';
printf("c: %c, *p: %c \n", ch, *p);
//打印日志:c: b, *p: b //二级指针
char **p2 = &p; //指向指针的指针,也占用8个字节
**p2 = 'd';
printf("ch: %c, *p: %c, **p2: %c \n", ch, *p, **p2);
//打印日志:ch: d, *p: d, **p2: d //三级指针
char ***p3 = &p2;
***p3 = 'f';
printf("ch: %c, *p: %c, **p2: %c, ***p3: %c \n", ch, *p, **p2, ***p3);
//打印日志:ch: f, *p: f, **p2: f, ***p3: f
printf("地址--》ch: %p, p: %p, p2: %p, p3: %p \n", p, p2, p3, &p3);
//打印日志:地址--》ch: 0x7fff5fbff76b, p: 0x7fff5fbff760, p2: 0x7fff5fbff758, p3: 0x7fff5fbff750
二、指针和数组
//指针和数组
int arr[] = {, , };
int *pi = arr;
//arr是数组名,也是数组地址,也是数组中第一个元素的地址
printf("arr: %p, pi: %p, arr[0]: %p \n", arr, pi, &arr[]);
//打印日志:arr: 0x7fff5fbff760, pi: 0x7fff5fbff760, arr[0]: 0x7fff5fbff760 //pi + 1 : 表示地址+1,本质是地址+该数据类型的字节个数; pi相当于数组名
printf("pi+1: %p, arr+1: %p \n", pi+, arr+);
printf("*(pi+1): %i, *(arr+1): %i, pi[1]: %i, arr[1]: %i \n", *(pi+), *(arr+), pi[], arr[]);
/** 打印日志:
pi+1: 0x7fff5fbff764, arr+1: 0x7fff5fbff764
*(pi+1): 18, *(arr+1): 18, pi[1]: 18, arr[1]: 18
*/ //字符指针和字符数组
char cArr[] = {'a', 'B', 'c', 'D', '\0'};
char *pc = cArr;
printf("cArr: %p, pc: %p, cArr[0]: %p \n", cArr, pc, &cArr[]);
printf("pc+1: %p, cArr+1: %p \n", pc+, cArr+);
printf("*(pc+1): %c, *(cArr+1): %c, pc[1]: %c, cArr[1]: %c \n", *(pc+), *(cArr+), pc[], cArr[]);
*(pc+) = 'Z';
printf("*(pc+1): %c, *(cArr+1): %c, pc[1]: %c, cArr[1]: %c \n", *(pc+), *(cArr+), pc[], cArr[]);
/** 打印日志:
cArr: 0x7fff5fbff743, pc: 0x7fff5fbff743, cArr[0]: 0x7fff5fbff743
pc+1: 0x7fff5fbff744, cArr+1: 0x7fff5fbff744
*(pc+1): B, *(cArr+1): B, pc[1]: B, cArr[1]: B
*(pc+1): Z, *(cArr+1): Z, pc[1]: Z, cArr[1]: Z
*/
三、常量指针和指向常量的指针
先来看几个变量,各自说说他们的区别和时什么类型的变量:
const int a1; //常量a1
int const a2; //常量a2, 这种写法和常量a1的写法是等效的关系 const int *a3; //指向整型常量的指针: 所指向的目标为常量(不能间接修改指向变量的值,可以从新赋值新地址)
int * const a4; //a4和a3 不同,a4是常量指针,指向整型的常量指针,即指针是常量
//(可以间接修改指向变量的值,但是不可重新赋值新变量地址) const int * const a5; //a5是指向整型常量的常量指针(既不能间接修改变量的值,也不可重新赋值新变量地址)
总结:1、const关键字写在数据类型的前面和后面是等效关系;
2、指向常量的指针:不能间接修改所指向变量的值,但是可以给指针重新赋值新地址
3、常量指针:可以间接修改指向变量的值,但是不能重新赋值新变量地址
4、指向常量的常量指针:有2个const修改,既不能间接修改变量的值,也不可重新赋值新变量地址
验证示例代码:
//1、指针变量,指针变量可间接修改值,指针变量也可重新赋值新变量地址
int a = , a2 = ;
int *ap = &a;
*ap = ; printf("测试1:a=%d, a地址:%x, *ap=%d, ap地址:%x, ap保存的地址:%x \n", a, &a, *ap, &ap, ap);
printf("变量a占用字节个数:%lu, 变量ap占用字节个数:%lu \n", sizeof(a), sizeof(ap));
//测试1:a=12, a地址:5fbff65c, *ap=12, ap地址:5fbff650, ap保存的地址:5fbff65c
//变量a占用字节个数:4, 变量ap占用字节个数:8
//验证一个问题:a占用的字节地址为5fbff65f-5fbff65c, ap占用的字节地址为:5fbff657-5fbff650
*ap = ;
printf("测试2:a=%d, a地址:%x, *ap=%d, ap地址:%x, ap保存的地址:%x \n", a, &a, *ap, &ap, ap);
//测试2:a=13, a地址:5fbff65c, *ap=13, ap地址:5fbff650, ap保存的地址:5fbff65c ap = &a2;
printf("*ap=%d, a2=%d, &a2=%x, ap=%x \n", *ap, a2, &a2, ap);
//*ap=22, a2=22, &a2=5fbff658, ap=5fbff658 //说明指针变量可以重新指向其他变量 //2、测试指向整型常量的指针
int c = , c2 = ;
int const *cp = &c; //指向整型常量的指针
printf("c1: c=%d, *cp=%d, &c=%x, cp=%x \n", c, *cp, &c, cp);
//c1: c=19, *cp=19, &c=5fbff63c, cp=5fbff63c
// *cp = 20; //报错:Read-only variable is not assignable
//指向整形常量的指针,不能间接修改变量的值,因为指向整型常量
c = ; //但是原变量自己可以直接修改自己的值
printf("c2: c=%d, *cp=%d, &c=%x, cp=%x \n", c, *cp, &c, cp);
//c2: c=20, *cp=20, &c=5fbff63c, cp=5fbff63c
cp = &c2;
printf("c3: c2=%d, *cp=%d, &c2 = %x, cp=%x \n", c2, *cp, &c2, cp);
//c3: c2=29, *cp=29, &c2 = 5fbff638, cp=5fbff638 //3、测试指向整型的常量指针
int d = , d2 = ;
int* const dp = &d;
printf("d1: d=%d, *dp=%d, &d=%x, dp=%x \n", d, *dp, &d, dp);
//d1: d=31, *dp=31, &d=5fbff63c, dp=5fbff63c
*dp = ;
printf("d2: d=%d, *dp=%d, &d=%x, dp=%x \n", d, *dp, &d, dp);
//d2: d=3, *dp=3, &d=5fbff63c, dp=5fbff63c // dp = &d2; //报错:Cannot assign to variable 'dp' with const-qualified type 'int *const'
//指向整型的常量指针,不能再重新赋值其他变量地址。但是可以间接修改当前指向的变量的值 //4、测试指向整型常量的常量指针:既不能间接修改变量的值,也不能重新赋值新的变量地址
int e = , e2 = ;
int const * const ep = &e;
// *ep = 48; //报错:Read-only vaiable is not assignable
// ep = &e2;//报错:Cannot assign to variable 'ep' with const-qualified type 'int *const' //5、指向常量的指针变量
int const b = ; //
// b = 30; //编译报错:Cannot assign to variable 'b' with const-qualified type 'const int'
int *bp = &b;
printf("b=%d, b地址: %x, bp保存的地址:%x, *bp: %d \n", b, &b, bp, *bp);
//b=50, b地址: 5fbff64c, bp保存的地址:5fbff64c, *bp: 50 *bp = ;
printf("修改后b=%d, b地址: %x, bp保存的地址:%x, *bp: %d \n", b, &b, bp, *bp);
//修改后b=50, b地址: 5fbff64c, bp保存的地址:5fbff64c, *bp: 88
/*
这个地方有点奇怪,b是常量,指针变量bp指向b, 间接通过指针bp修改变量的值,
但是最后打印结果是:*bp的值变了,b的值没有变(b是常量,指针变量bp指向b), 而且bp保存的地址和b的地址还是保持一样
这究竟是为啥?怎么理解 ?
变量被const修饰时,就复制了其值出来放到常量表中(由系统维护)
但是每次取常量时,它是从常量表找到以前的值,而不是再次读内存。
而指针变量bp可以修改指向地址里面的值。
不知这样理解是否正确?
*/
四、指针和字符串的关系,变量内存栈区和常量区区别
测试字符数组,指向字符数组的指针,和指向字符串的指针的区别;
字符数组和指针存储的字符串在内存中的存储位置:即栈区存储和常量区存储的区别
//1、字符数组
char cs[] = "test";
printf("%s, 第一个字符:%c, 地址:%p, %p, %p\n", cs, cs[], cs, &cs[], &cs[]);
cs[] = 'A';
printf("修改后的字符数组:%s, 第一个字符:%c, 地址:%p, %p, %p\n", cs, cs[], cs, &cs[], &cs[]);
/*
test, 第一个字符:t, 地址:0x7fff5fbff63b, 0x7fff5fbff63c, 0x7fff5fbff63d
修改后的字符数组:Aest, 第一个字符:A, 地址:0x7fff5fbff63b, 0x7fff5fbff63c, 0x7fff5fbff63d
*/ //2、指向字符串的指针
char *cp = cs;
// printf("%s", *cp); //这样写会挂掉,因为*cp取出的数组中第一个字符
printf("%s, %c, %c, %c, %c\n", cp, *cp, *(cp+), *(cp+), *(cp+));
printf("%p, %p, %p\n", cp, cp+, cp+);
/*
Aest, A, A, e, s
0x7fff5fbff63b, 0x7fff5fbff63c, 0x7fff5fbff63d 解释: cp表示数组名,取出字符数组所有字符;*cp == *(cp+0) 表示取出数组中第一个字符的值,后面以此类推;
cp表示字符数组中第一个字符的地址, cp+1表示第二个字符的地址, 后面以此类推
cp格式化时用%s表示取出整个字符数组的所有字符,用%p表示取出字符数组的最低位地址(即第一个字符的地址)
*/
//修改字符数组的值
*cp = 'B';
*(cp+) = 'C';
*(cp + ) = 'd';
printf("通过指针修改字符数组的值:%s, %c\n", cp, *(cp+)); //直接字符指针指向常量字符串
char *cp2 = "daxia";
printf("cp2: %s, %p, %c, %p, %c, %p\n", cp2, cp2, *(cp2+), cp2+, *(cp2+), cp2+);
//cp2: daxia, 0x100001aab, a, 0x100001aac, x, 0x100001aad
// *(cp2 + 1) = 'G'; //野指针错误:因为常量字符串不允许修改 //测试字符数组的存储区和常量字符串的存储地方
char cs1[] = "hello";
char cs2[] = "hello";
char *p1 = "hello";
char *p2 = "hello";
printf("cs1: %p, cs2: %p, p1: %p, p2: %p\n", cs1, cs2, p1, p2);
//cs1: 0x7fff5fbff61a, cs2: 0x7fff5fbff614, p1: 0x100001a9e, p2: 0x100001a9e
//为什么?说明:字符数组存在内存中的栈区,常量字符串存在内存中的常量区
/*
如果通过数组来保存字符串,那么字符串变量可以修改,存在内存栈中,当作用域结束时自动释放该变量;
如果通过指针来保存字符串,那么字符串是一个常量不能修改,保存在内存常量区,不会被释放,多个相同的值对应的地址相同;
*/
五、数组越界造成的访问不属于自己的内存空间
六、引用数据类型和基本数据类型,形参和实参
七、字符串和字符数组
八、字符串常用函数(长度、拷贝、追加、比较)
demo地址:https://github.com/xiaotanit/Tan_TestIOS/tree/master/Tan_LocationMove
C语言之指针变量的更多相关文章
- C语言的指针变量
C语言的指针变量 在C语言中,变量是固定范围的存储空间,它存储的是赋给他的值, 比如: ; /* 这里是定义一个整型变量a,并把12这个值存储在a的地址空间上 这个地址空间是系统随机分配的,对用户是透 ...
- C语言函数指针变量和指针函数以及指针数组
C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址.我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数.然后通过指针变量就可以找到并调用这 ...
- C语言_指针变量的赋值与运算,很详细
指针变量的赋值 指针变量同普通变量一样,使用之前不仅要定义说明, 而且必须赋予具体的值.未经赋值的指针变量不能使用, 否则将造成系统混乱,甚至死机.指针变量的赋值只能赋予地址, 决不能赋予任何其它数据 ...
- C语言中指针变量如何向函数中传递
指针变量存储的是地址,所以在函数调用的时候我们能否将指针变量传递给函数?如果不知道结果,那我们可以直接问电脑,输入如下一段代码. void GetMemory(char *p) { p = (char ...
- C语言中指针变量的加减运算
1.指针变量中存放的是地址值,也就是一个数字地址,例如某指针变量中的值是0x20000000,表示表示此指针变量存放的是内存中位于0x20000000地方的内存地址.指针变量可以加减,但是只能与整型数 ...
- [日常] C语言中指针变量
CPU 访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址.编译和链接过程的一项重要任务就是找到这些名称所对应的 ...
- C语言使用指针变量指向字符串,对字符串进行处理后再将指针移向开头为什么不能输出?(使用Dev-c++进行编译)
# include <stdio.h> # include <stdlib.h> int main() { char *point_1="aaaaaabbbbbbzz ...
- C语言中指针占据内存空间问题
以前一直有个疑问,指向不同类型的指针到底占用的内存空间是多大呢? 这个问题我多次问过老师,老师的答案是"指向不同类型的指针占据的内存空间大小不同",我一直很之一这个答案,今天我就做 ...
- 这样子来理解C语言中指针的指针
友情提示:阅读本文前,请先参考我的之前的文章<从四个属性的角度来理解C语言的指针也许会更好理解>,若已阅读,请继续往下看. 我从4个属性的角度来总结了C语言中的指针概念.对于C语言的一个指 ...
随机推荐
- 带你十分钟快速构建好 SpringBoot + SSM 框架
目前最主流的 java web 框架应该是 SSM,而 SSM 框架由于更轻便与灵活目前受到了许多人的青睐.而 SpringBoot 的轻量化,简化项目配置, 没有 XML 配置要求等优点现在也得到了 ...
- iperf3 不支持双工模式
iperf 2.05的时候,客户端可以使用参数"-d"来进行双工测试,先测试发送,client向server发送数据,等到测试时间结束后(默认为10s,可以通过-t选项来更改),然 ...
- $Django setting.py配置 ,GET、POST深入理解,三件套,orm对象关系映射简介
1 django中app的概念: 大学:----------------- 项目 信息学院 ----------app01 物理学院-----------app02 ****强调***:创建的每一 ...
- 转载:Practical UML™: A Hands-On Introduction for Developers
原文:http://edn.embarcadero.com/article/31863 By: Randy Miller Abstract: This tutorial provides a quic ...
- Ex4_21 最短路径算法可以应用于货币交易领域..._第十二次作业
(a) 建立一个有向图G(V,E),每个顶点表示一种货币,两个顶点之间的边权的大小ex[u][v]表示两种货币之间的汇率,若要找一个最有利的兑换序列,把货币s兑换成货币t,即在若干种兑换序列中选择 ...
- WebSocket异步通讯,实时返回数据相关问题论坛
https://stackoverflow.com/questions/23773407/a-websockets-receiveasync-method-does-not-await-the-ent ...
- Confluence 6 配置白名单
Confluence 管理员可以通过添加 URLs 到白名单选择出入的链接和使用 RSS 宏,HTML 包含宏和小工具中的内容. 如果一个内容被添加到 Confluence 系统中,但是这个 URLs ...
- 关于Sublime text 3如何编辑less并转(编译)成css文件
今天开始学习使用less这个强大方便的前端工具,本来是考虑用koala(专门编辑less的软件)来使用less的,但是发现sublime编辑器也可以实现对less的编译及高亮显示代码,这样既能少用一个 ...
- Mysql哪些字段适合建立索引
数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特 ...
- Linux 用户(user)和用户组(group)管理概述
一.理解Linux的单用户多任务,多用户多任务概念: Linux 是一个多用户.多任务的操作系统:我们应该了解单用户多任务和多用户多任务的概念: 1.Linux 的单用户多任务:单用户多任务:比如我们 ...