C语言强化——指针
目录
- 相关概念
- 数组与函数
- 栈空间和堆空间的差异
- 指针常量与常量指针
- 指针数组与数组指针
- 二级指针
- 二级指针的传递
- 二级指针的偏移(索引式排序)
相关概念
- 指针的大小,在32系统上是4个字节;在64位系统上,是8个字节。 (32位系统,32根地址线,寻址空间为232次方,最大地址值为232-1,只需4个字节即32位存储地址。)
void test3()
{//指针的大小,在32系统上是4个字节;在64位系统上,是8个字节
int number = 10;
int * p = &number;
printf("sizeof(p) = %d\n", sizeof(p));
}
野指针:定义指针未初始化或者释放空间后指针未置NULL
内存泄漏:申请空间后未释放(malloc/free)
内存踩踏:使用了不属于某一个变量或者指针开辟出来的空间左值和右值
lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。
rvalue通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是在不在内存中占有确定位置的表达式。
数组与函数
- 函数参数传递数组
- 一维数组长度无法传递。
- 一维数组作为参数,本质上传递的是指针(例子中只会打印一个数字!)
void test0(int arr[]) {
int *p = arr;
for (int idx = 0; idx != sizeof(arr) / sizeof(int); ++idx) {
printf("%2d", *p++);
}
printf("\n");
}
int main() {
int arr[] = { 1,2,3,4,5 };
test0(arr); //传入的是指针
return 0;
}
- sizeof() 函数:
一维数组打印
void test1()
{
int arr[] = { 1, 2, 3, 4, 5 };
int * p = arr;
for (int idx = 0; idx != sizeof(arr) / sizeof(int); ++idx) {
printf("%2d", *p++);
}
printf("\n");
}
二维数组打印
#include<stdio.h>
#include<string.h>
void print(int(*p)[4], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
}
}
int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
print(arr, 3);
return 0;
}
栈空间和堆空间的差异
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//函数栈空间释放后,函数内所有局部变量消失
char* print_stack() {
char c[] = "I am a print_stack.";
puts(c);
return c;
}
//堆空间不会因函数执行结束而释放
char* print_malloc() {
char* p;
p = (char*)malloc(20);
strcpy(p, "I am a print_malloc.");
puts(p);
return p;
}
int main() {
char* p;
p = print_stack(); //数据在栈空间
puts(p); //打印异常
p = print_malloc(); //数据在堆空间
puts(p);
return 0;
}
指针常量与常量指针
- 指针常量:不能改变指针的指向,只能改变指向的值。
int arr[10] = { 1, 2, 3, 4, 5 };
int * const p = arr; //指针常量(const pointer)
int number = 10;
//p = &number; //error 不能改变指针的指向
*p = 10;//ok 可以改变指针所指变量的值
- 常量指针:指向常量的指针,不能改变指针指向的值,只能改变指针的指向
const int * p1 = &number; //常量指针(pointer to const)
p1 = arr; //p1可以改变指向
//*p1 = 10;//error 不能修改p1所指空间的内容
常量指针的另一种写法 int const * p1 = &number;
指针数组与数组指针
- 定义
int (*p1)[5];//指向一个大小为5的数组,数组里面存放的是整型数据
int * p2[5];//指针数组
&arr和arr的地址相同,不同的是arr+0是int型指针,而&arr是数组指针。
&+数组名的使用
void test1()
{
int arr[] = { 1, 2, 3, 4, 5 };
int * p = arr;
p = &arr;
printf("%d\n", *(&arr + 1)); //&arr 代表的是数组指针类型,越界
printf("%d\n", *(arr + 1)); //打印2
printf("*p = %d\n", *p); //打印1
}
其中*(&arr+1)偏移了整个数组的大小,解引用后越界!同理如果是二维数组也会偏移整个二维数组的大小。
- 数组指针的应用
“二维数组的实现是通过二级指针实现的”,这是错误的!二维数组通过两次偏移到获取到数组中的某一个元素,所使用的指针是数组指针,数组指针是一级指针。
#include<stdio.h>
#include<string.h>
void print(int(*p)[4], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
}
}
//数组指针用于二维数组的传递和偏移
int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
int b[4] = { 1,2,3,4 };
int i = 10;
int(*p)[4];//定义一个数组指针
p = arr;
print(p, 3);
return 0;
}
强转实现4行3列输出
#include<stdio.h>
#include<string.h>
void print(int(*p)[3], int row) {
int i, j;
for (int i = 0;i < row;++i) {
for (int j = 0;j < sizeof(*p) / sizeof(int);++j) {
printf("%3d", p[i][j]);
}
printf("\n");
}
}
//数组指针用于二维数组的传递和偏移
typedef int(*ptype)[3];
int main() {
int arr[3][4] = { 1,3,5,7,2,4,6,8,10,11,12,13 };
int b[4] = { 1,2,3,4 };
int i = 10;
int(*p)[3];//定义一个数组指针
print((ptype)arr, 4);
return 0;
}
二级指针
- 二级指针的传递
下 面 有 一 个 实 例 , 整 型 指 针 pi, 指 向 整 型 变 量 i, 整 型 指 针 pj 指 向 整 型 变 量 j , 通 过 子
函 数 change, 我 们 想 改 变 指 针 变 量 pi 的 值 , 让 其 指 向 j, 我 们 知 道 c 语 言 的 函 数 调 用 是 值 传
递 , 因 此 要 想 在 change 中 改 变 变 量 pi 的 值 , 那 么 必 须 把 pi 的 地 址 传 递 给 change,pi是一级
指针,&pi的类型即为2级指针。
要 想 在 子 函 数 改 变 一 个 变 量 的 值 , 必 须 把 该 变 量 的 地 址 传 进 去
要 想 在 子 函 数 改 变 一 个 指 针 变 量 的 值 , 必 须 把 该 指 针 变 量 的 地 址 传 进 去,二 级 指 针 传 递
对 于 二 级 指 针 的 传 递 使 用 场 景 , 把 握 两 点 , 第 一 . 二 级 指 针 变 量 定 义 是 在 形 参 , 第 二
在 调 用 函 数 中 往 往 不 定 义 二 级 指 针 , 如 果 定 义 , 初 始 化 注 意 是 一 级 指 针 的 取 地 址 。
#include<stdio.h>
#include<stdlib.h>
void change(int **p2, int *pb) {
*p2 = pb;
}
//要 想 在 子 函 数 改 变 一 个 变 量 的 值 , 必 须 把 该 变 量 的 地 址 传 进 去
//要 想 在 子 函 数 改 变 一 个 指 针 变 量 的 值 , 必 须 把 该 指 针 变 量 的 地 址 传 进 去
int main() {
int i = 10;
int j = 5;
int *pi, *pj;
pi = &i;
pj = &j;
printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj); //10 5 10 5
change(&pi, pj);
printf("i=%d, j=%d, *pi=%d, *pj=%d\n", i, j, *pi, *pj); //10 5 5 5
return 0;
}
- 二级指针的偏移
一 级 指 针 的 偏 移 服 务 于 数 组 , 整 型 一 级 指 针 服 务 于 整 型 数 组 , 所 以 二 级 指 针 的 偏 移 也 服
务 于 数 组 , 服 务 对 象 为 指 针 数 组 。
- 应用:索引式排序
#include<stdio.h>
#include<string.h>
//void print(char *p[], int len) 二级指针的偏移服务的是指针数组
void print(char **p, int len) { //二级指针每次偏移4个字节(因为一级指针在32位系统中占4个字节)
int i;
for (i = 0;i < len;++i) {
puts(p[i]);
}
}
void printArr(char(*p)[10], int row) {
int i;
for (i = 0;i < row;++i) {
puts(p[i]);
}
}
int main() {
char b[5][10] = { "php","python","linux","unix","java" };
char *p[5]; //指针数组
int i, j;
char *tmp;
for (i = 0;i < 5;++i) { //让指针数组中的每一个指针指向一个字符串
p[i] = b[i];
}
print(p, 5);
printf("--------------------\n");
printArr(b, 5);
printf("--------------------\n");
for (int i = 4;i > 0;--i) {
for (int j = 0;j < i;++j) {
if (strcmp(p[j], p[j + 1]) > 0) {
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
print(p, 5);
return 0;
}
主函数内定义二级指针实现:
#include<stdio.h>
#include<string.h>
//void print(char *p[], int len) 二级指针的偏移服务的是指针数组
void print(char **p, int len) { //二级指针每次偏移4个字节(因为一级指针在32位系统中占4个字节)
int i;
for (i = 0;i < len;++i) {
puts(p[i]);
}
}
void printArr(char(*p)[10], int row) {
int i;
for (i = 0;i < row;++i) {
puts(p[i]);
}
}
int main() {
char b[5][10] = { "php","python","linux","unix","java" };
char **p; //等价于char *p[];
int i, j;
p = (char**)malloc(20); //一级指针强制类型转换为二级指针,使用动态的指针数组
char *tmp;
for (i = 0;i < 5;++i) { //让指针数组中的每一个指针指向一个字符串
p[i] = b[i];
}
print(p, 5);
printf("--------------------\n");
printArr(b, 5);
printf("--------------------\n");
for (int i = 4;i > 0;--i) {
for (int j = 0;j < i;++j) {
if (strcmp(p[j], p[j + 1]) > 0) {
tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
print(p, 5);
return 0;
}
- 传递二级指针修改指针本身的值
#include <stdio.h>
//传递二级指针修改指针本身的值
void swap(int ** p1, int ** p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
printf("函数内:*p1 = %d, *p2 = %d\n", **p1, **p2);
}
void test6()
{
int number1 = 3, number2 = 4;
int * p1 = &number1;
int * p2 = &number2;
printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
swap(&p1, &p2);
printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
}
int main(void)
{
test6();
return 0;
}
C语言强化——指针的更多相关文章
- 浅谈c语言的指针
对于非计算机专业的同学,c语言的指针往往就是老师的一句“指针不考“就带过了.c语言的指针号称是c语言的灵魂,是c语言中最精妙的部分. 指针本质上也是变量,也就是一段内存,只是他的特殊之处是他存储的数据 ...
- C#委托与C语言函数指针及函数指针数组
C#委托与C语言函数指针及函数指针数组 在使用C#时总会为委托而感到疑惑,但现在总新温习了一遍C语言后,才真正理解的委托. 其实委托就类似于C/C++里的函数指针,在函数传参时传递的是函数指针,在调用 ...
- C语言二重指针与malloc
(内容主要源于网上,只是加入了些自己的剖析) 假设有一个二重指针: char **p; 同时有一个指针数组 char *name[4]; 如何引用p呢? 首先我们有程序代码如下 #include &l ...
- C语言函数指针基础
本文写的非常详细,因为我想为初学者建立一个意识模型,来帮助他们理解函数指针的语法和基础.如果你不讨厌事无巨细,请尽情阅读吧. 函数指针虽然在语法上让人有些迷惑,但不失为一种有趣而强大的工具.本文将从C ...
- 为什么C/C++语言使用指针
这是参加面试时,面试官问的一道开放性题目. 问题是:为什么C/C++语言使用指针? 这个问题一问出来,直接被面试官秒杀了,面试官大神,你怎么不按套路出牌啊? 说好的malloc和new的区别呢?说好的 ...
- C语言的指针变量
C语言的指针变量 在C语言中,变量是固定范围的存储空间,它存储的是赋给他的值, 比如: ; /* 这里是定义一个整型变量a,并把12这个值存储在a的地址空间上 这个地址空间是系统随机分配的,对用户是透 ...
- Android For JNI(五)——C语言多级指针,结构体,联合体,枚举,自定义类型
Android For JNI(五)--C语言多级指针,结构体,联合体,枚举,自定义类型 我们的C已经渐渐的步入正轨了,基础过去之后,就是我们的NDK和JNI实战了 一.多级指针 指针的概念我们在前面 ...
- “对外部(局部)变量的访问”是C语言函数指针的最大弱点
1.“对外部(局部)变量的访问”是C语言函数指针的最大弱点 . #include <stdio.h> #include <stdlib.h> /* 结构体定义 */ struc ...
- go语言学习--指针的理解
Go 的原生数据类型可以分为基本类型和高级类型,基本类型主要包含 string, bool, int 及 float 系列,高级类型包含 struct,array/slice,map,chan, fu ...
随机推荐
- python 字符串,列表,元组,字典相互转换
1.字典 dict = {'name': 'Zara', 'age': 7, 'class': 'First'} 字典转为字符串,返回:<type 'str'> {'age': 7, 'n ...
- LG4781 【模板】拉格朗日插值
题意 题目描述 由小学知识可知,$n$个点$(x_i,y_i)$可以唯一地确定一个多项式 现在,给定$n$个点,请你确定这个多项式,并将$k$代入求值 求出的值对$998244353$取模 输入输出格 ...
- 【SpringBoot】SpringBoot热部署和配置文件自动注入实战
========================3.SpringBoot热部署devtool和配置文件自动注入实战 ============================ 1.SpringBoot2 ...
- Cassandra基础3
cassandra读性能优化:1.禁用read repair每次读操作,无论读请求设置读一个节点还是多个节点,cassandra返回给客户端最新的数据后,都会后台对比所有副本的数据并对差异数据进行修复 ...
- Scala中的Map使用例子
Map结构是一种非常常见的结构,在各种程序语言都有对应的api,由于Spark的底层语言是Scala,所以有必要来了解下Scala中的Map使用方法. (1)不可变Map特点: api不太丰富 如果是 ...
- 你不知道的JavaScript(中卷) (Kyle Simpson 著)
第一部分 类型和语法 第1章 类型 1.1 类型 1.2 内置类型 1.3 值和类型 1.3.1 undefined和undeclared 1.3.2 typeof Undeclared 1.4 小结 ...
- LoadRunner手写脚本、检查点、集合点、事务、思考时间
手写脚本 什么时候要手写? 可以有条件手写脚本的场景有两类: 有接口说明文档 没有借口说明文档,要去录制,录制不了,抓包手写 所需函数 我们这里讲的例子是基于 http 协议的,也是常见的两种请求类型 ...
- day10 内容大纲
01 去年内容回顾 01 去年内容回顾 *args **kwargs: 万能参数,动态参数 * 魔性用法: 函数的定义时,* ** 聚合. 函数的执行时,* ** 打散. 形参顺序: 位置参数,*ar ...
- TypeScript 之 书写.d.ts文件
https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/Writing%20Definition%20Files.html ...
- 怎么理解Python画图中的X,y
X_outliers=np.array([[3.4, 1.3], [3.2, 0.8]]) y_outliers=np.array([0, 0]) 要明白X,y不再是我们高中时候学的x,y轴的坐标:首 ...