开发一个网站需要的技术,自己建立网站怎么建,南通专业制作网站,号码认证目录 一、内存和地址
1、生活中的例子
2、内存的关系
二、指针变量和地址
1、符号#xff0c;%p占位符
2、一个简单的指针代码。
3、理解指针
4、解引用操作符
5、指针变量的大小。
三、指针变量类型的意义
1、指针解引用的作用
2、指针指针
3、指针-指针
4…目录 一、内存和地址
1、生活中的例子
2、内存的关系
二、指针变量和地址
1、符号%p占位符
2、一个简单的指针代码。
3、理解指针
4、解引用操作符
5、指针变量的大小。
三、指针变量类型的意义
1、指针解引用的作用
2、指针指针
3、指针-指针
4、void*指针
四、const修饰指针
1、const修饰变量
2、const修饰指针变量
1、没有const
2、const在*左边
3、const在*右边
4、const在*两边
五、指针运算
1、指针-整数
2、指针-指针
3、指针的关系运算
六、野指针
1、野指针的成因
1、未初始化
2、越界访问
3、指针指向空间的释放
2、如何避免野指针
七、assert断⾔
八、指针的使⽤和传址调⽤
1、strlen的模拟实现
2、传值调用传值调用
1、传值调用
2、传址调用 hello大家好今天我们来讲一下C语言中指针的知识点指针这部分内容很多容易记混而且有时候写代码也想不到这就需要有一个良好的指针基础我尽全力把指针用我们已经学习过的知识来讲述让学习者都可以很快的理解并且可以使用我所讲指针给出的例子来练习。
那么我们就开始学习吧
一、内存和地址
1、生活中的例子
学习指针我们就要知道内存和地址的关系。
下面图中我们可以把这一栋楼看做内存每个房间可以看做地址。 如果我们想去到楼里的特定房间我们就要挨个房间寻找我们想要去的特定房间里面然后我们再去到另外一个特定的房间里面我们还需要挨个寻找这样效率会非常慢。
聪明的同学已经开始想到了我们可以把每个房间都设置门牌号这样我们就可以快速去到我们想去的房间了。
结论门牌号地址指针
2、内存的关系
我们可以把每把内存给划分为内存单元一个内存单元大小等于一个字节所以每个房间就可以看着是字节。
常见的存储单位bit比特- Byte(字节) - KB - MB - GB - TB - PB
1字节 8bit位 二、指针变量和地址
1、符号%p占位符
取地址符号可以让我们获取地址。
%p想打印出地址就需要使用%p这个占位符。
int main( )
{int a 10;printf(%p\n, a);return 0;
}输出
2、一个简单的指针代码。
int main()
{int a 10;int* p a;return 0;
}通过调试vs我们看见pa。 在这段代码中我们设置了a10我们通过*p指向了a的地址此时p就等于a的地址。我们就可以通过*p来修改a中的地址。
3、理解指针
int a 10;
int * pa a;pa左边写的是int**说明是pa的指针变量而int在说明pa指向的是一个整型类型的对象。
4、解引用操作符
解引用操作符就是“ * ”。
int main()
{int a 10;int* p a;*p 20;printf(%d , *p); //输出20return 0;
}这段代码中我们通过*p20修改a的数值。尽管int* p a;这一行代码我们已经获取了a的地址但是我们还是需要通过操作符“ * ”来访问或修改这样地址指向的值。p中存放的内容是a的地址如果我们直接写p20程序修会报错因为p是一个指针不能直接存放一个整数。只有我们使用*p我们才能直接访问p指向的内存空间即a的内存空间从而修改变量中的a此时a的数值也会跟着改变。
5、指针变量的大小。
指针变量的大小是取决于我们使用的是32位平台还是64位平台。
int main()
{printf(%d , sizeof(char*));printf(%d , sizeof(int*));printf(%d , sizeof(double*));printf(%d , sizeof(float*));return 0;
}32位平台输出
64位平台输出
在32位下指针的大小位4个字节。
在64位下指针的大小位8个字节。
三、指针变量类型的意义
1、指针解引用的作用
int main()
{int a 0x11223344;char* p (char*)a;*p 0;return 0;
}*p运行前a地址 *p运行后a地址 为什么只有一个字节等于0因为*p是一个char类型char类型只占一个字节。
只有*p跟a是一个类型的时候才可以把a10。
2、指针指针
int main()
{int a 10;char* p1 (char*)a;int* p2 a;printf(%p\n, a);printf(%p\n, p1);printf(%p\n, p11);printf(%p\n, p2);printf(%p\n, p21);return 0;
}输出 我们可以看到p1指针是char类型p2是int类型当把指针1的时候p1和p2所得到的地址是不一样的p1加1字节p2加4字节这是因为指针的类型是不一样的。
结论指针的类型决定的指针向前的步子是多大。
3、指针-指针
int main()
{int a 10;char* p1 (char*)a;int* p2 a;printf(%p\n, a);printf(%p\n, p1);printf(%p\n, p1 - 1);printf(%p\n, p2);printf(%p\n, p2 - 1);return 0;
}输出
4、void*指针
int main()
{int a 10;char* p a;*p 20;return 0;
}char* p a;由于我们使用char类型接收int类型导致编译器提示错误。
void*可以接收任意指针charintlongdouble等等
int main()
{int a 10;void* p a;return 0;
}但是void*是无法修改指针变量的
int main()
{int a 10;int b 20;void* p1 a;void* p2 b;*p1 30;*p2 40;return 0;
}那么void*类型的指针有什么用呢
void*是使用函数参数部分用来接收任意不同类型的数据可以实现泛编程效果使得一个函数可以出来多个数据类型。
四、const修饰指针
1、const修饰变量
const这个函数可以让变量中避免被修改。
int main()
{const int a 10;a 20;return 0;
}但是我们仍然可以使用指针来修改a变量
int main()
{const int a 10;int* p a;*p 20;printf(%d, *p);return 0;
}输出
那么就有人问了这有什么意思这不就是防君子不防小人吗那么我们如果才可以防止a被修改呢
2、const修饰指针变量
这些有什么区别呢
int* p; //没有const
const int* p; //const在*左边
int const* p; //const在*左边
int* const p; //const在*右边
int const * const p; //const在*两边
1、没有const
void test1()
{int n 10;int m 20;int* p n;*p 20;p m;
}在没有const限制的条件下我们可以修改指针*p的变量p也可以获得m的地址
2、const在*左边
const在*左边有两种写法这两种写法都是正确的
const int* p a;
int const* p a;void test1()
{int n 10;int m 20;const int* p n;*p 20;p m;
}在const限制的条件下我们不可以修改指针*p的变量但是p可以获得m的地址.
3、const在*右边
void test1()
{int n 10;int m 20;int* const p n;*p 20;p m;
}const限制的条件下我们可以修改指针p的数值但是p不可以获得m的地址。
4、const在*两边
void test1()
{int n 10;int m 20;int const* const p n;*p 20;p m;
}const限制的条件下我们不可以修改指针p的数值p也不可以获得m的地址。 总结
const在左边的时候修饰的是指针指向的内容保证指向内容不能被指针修改但是指针变量本身的内容是可变的。const在右边的时候修改是是指针变量本身保证指针指向的内容是可以被修改的但是指针变量本身是无法被修改的 五、指针运算
int arr[10] { 1,2,3,4,5,6,7,8,9,10 };
下图是数组中每一个元素的下标 1、指针-整数
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p arr[0];int i 0;int sz sizeof(arr) / sizeof(arr[0]);for (i 0; i sz; i){printf(%d , *(p i));//指针整数}return 0;
}指针*p的类型是int地址是arr[0]下标是0通过pi我们就可以打印出指针指向每一个元素下标地址通过解引用我们从而获得打印数组。
我们之前学习的是printf(%d , arr[i]);这样打印出数组但是在编译器底层是通过指针来打印的*(p i) arr[i]。
2、指针-指针
#include stdio.h
int my_strlen(char* s)
{char* p s;int count 0;while (*p ! \0){count;p;}return count;
}int main()
{printf(%d\n, my_strlen(abcdef));return 0;
}输出字符串长度 ---------------------------------------------------------------------------------------------------------------------------------
int my_strlen(char* s)
{char* p s;while (*p ! \0)p;return p - s; //当p结束是指向字符串的末尾所以我们用末尾-初始值就是字符串长度
}int main()
{printf(%d\n, my_strlen(abc));return 0;
}3、指针的关系运算
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p arr[0];int sz sizeof(arr) / sizeof(arr[0]);while (p arr[sz]) //指针的⼤⼩⽐较{printf(%d , *p);p;}return 0;
}while (p arr[sz])这一行代码我们是利用地址来进行比较的当p这个地址小于arr[sz]地址的时候那么就不允许。
六、野指针
指针指向实际方向的内容是否有效超出指针指向变量部分的内容或者是栈帧销毁的内容会成为野指针野指针的数值是一个随机值。
1、野指针的成因
1、未初始化
int main()
{int* p1;*p 10;return 0;
}2、越界访问
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,0 };int* p arr[0];for (int i 0; i 11; i){printf(%d , *(pi));}return 0;
}输出 在这个一维数组打印中指针超出了指向数组的值造成了随机值这就是野指针。
3、指针指向空间的释放
int test()
{int n 100;return n;
}int main()
{int* p test();printf(%d, *p);return 0;
}在这一段代码中*p也是一个野指针这是为什么因为我们创建的test函数在运行完以后就会销毁所以*p指向的是ntest函数已经被销毁了*p就没有地址了导致*p变成了野指针。
2、如何避免野指针
int main()
{int a 10;int* p1 a;int* p2 NULL;return 0;
}我们可以在创建的int* p里放入NULL就可以避免野指针出现。
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,0 };int* p arr[0];for (int i 0; i 11; i){*(p) i;}
//p已经越界把NULL赋值给pp NULL; //把野指针的数值等于NULLp arr[0]; //重新赋值pif (p!NULL){printf(haha\n);}return 0;
}七、assert断⾔
assert头文件
assert (p!NULL);
assert是一个对程序员很友好的功能如果assert这个程序符合调价那么就继续运行如果不符合那就会提示错误在哪些地方。
如果代码没有任何问题以后可以在头文件上面写
#define NDEBUG#include assert.h
int main()
{int age 11;// 使用assert检查年龄是否大于18岁assert(age 18);printf(年龄大于18岁。\n);return 0;
}八、指针的使⽤和传址调⽤
1、strlen的模拟实现
方法一
int my_strlen(const char* str)
{int count 0;assert(str);while (*str){count;str;}return count;
}int main()
{int len my_strlen(abcdef);printf(%d\n, len);return 0;
}方法二
int my_strlen(const char* str)
{char* p str;assert(str);while (*p){p;}return p-str;
}int main()
{int len my_strlen(abcdef);printf(%d\n, len);return 0;
}输出
2、传值调用传值调用
1、传值调用
void Swap1(int x, int y)
{int tmp x;x y;y tmp;
}int main()
{int a 10;int b 20;printf(交换前a%d b%d\n, a, b);Swap1(a, b);printf(交换后a%d b%d\n, a, b);return 0;
}为什么明明在函数中交换了a和b的数值为什么没有交换成功 2、传址调用
void Swap2(int* px, int* py)
{int tmp 0;tmp *px;*px *py;*py tmp;
}
int main()
{int a 10;int b 20;printf(交换前a%d b%d\n, a, b);Swap2(a, b);printf(交换后a%d b%d\n, a, b);return 0;
}输出 指针并不是跟形参一样是拷贝指针指向的数据就是ab这两个地址当函数Swap2中pxpy中数据进行交换的时候交换的就是main函数中a,b中真实的数据。