首页 技术 正文
技术 2022年11月20日
0 收藏 490 点赞 3,835 浏览 8053 个字

指针是c语言的灵魂

—————————————————————————–

# include <stdio.h>

int main(){

int *p;  //p是变量名,int *表示p变量存放的是int类型变量的地址,p是一个指针变量

int i = 3;

//p = i; 这样写是错误的

//p = 4; 这样写是错误的

p = &i;  //将i变量的地址给p变量

//p保存了i的地址,因此p指向i,修改p的值不影响i的值,修改i的值也不影响p的值

return 0;

}

—————————————————————————–

# include <stdio.h>

int main(){

int *p; //不表示定义了一个名字叫做 *p的变量

//应该这样理解:p是变量名,p变量的数据类型是 int *类型

//int *类型实际就是存放int变量地址的类型

int i = 3;

p = &i;

printf("*P = %d\n", *p);

printf("i = %d\n", i);

return 0;

}

1.如果p是个指针变量,并且p存放了普通变量i的地址,则p指向了普通变量

2.*P 完全等同于 普通变量i  (有i出现的地方都可以替换成*p)

——————————————————————————

指针和指针变量的区别:

1.指针就是地址,地址就是指针

地址就是内存单元的编号,所以指针就是内存单元的编号

2.指针变量存放地址的变量

指针变量是存放指针的变量,也就是说指针变量是存放内存单元编号的变量

3.指针和指针变量是两个不同的概念

但是要注意通常我们叙述时会把指针变量简称为指针,实际它们的含义不一样

一、指针的重要性

1.表示一些复杂的数据结构

2.快速的传递数据

3.使函数返回一个以上的值(函数只能返回一个值)

———————————————————

# include <stdio.h>

int f(int a, int b);

void g(int * p ,int * q);

int main(void){

int a = 100;

int b = 200;

// a = f(a, b);

g(&a, &b);

printf("a = %d, b = %d\n", a, b);

return 0;

}

//只能修改一个值

int f(int a, int b){

return 1;

}

//这样被调函数可以修改主调函数一个以上的值

void g(int * p ,int * q){

*p = 1;

*q = 2;

}

———————————————————

4.能直接访问硬件

5.能够方便的处理字符串

6.是理解面向对象语言中引用的基础

总结:指针是c语言的灵魂,是和其他语言的区别

二、指针的定义

地址

内存单元的编号,地址是从零开始的非负整数。

范围:

     控制总线

CPU <—->    数据总线  <—–>  内存条

     地址总线

控制线控制数据传输的方向 

数据线是传输数据

地址线是确定是控制哪个内存单元

cup<—–>  数据总线 <—–> 内存条

一根线控制两个 0和1

两根线控制四个

n根线控制2的n次方个单元(字节)(每个单元是8位)

32位机 2×10^32

1G=2X10^30B(字节)

2×10^32 ~= 2X10^30*4  所以内存最大4G

4G  [0——4G-1]

指针

指针就是地址,地址就是指针,指针变量就是存放内存单元编号的变量。

指针和指针变量是两个不同的概念

指针本质就是一个操作受限(不能运算,只是编号)的非负整数(地址)

三、指针的分类

1.基本类型指针

 //就是上面的代码

————————————————————————–

指针常见错误1

# include <stdio.h>

int main(void){

int *p;  //p指向一个垃圾值的地址,也就是一个垃圾地址

int i = 5;

*p = i;  // 就是将i的值给了一个不知道的地址,这样写不对

printf("%d\n", *p);

return 0;

}

常见错误2

# include <stdio.h>

int main(void){

int i = 5;

int *p;

int *q;

p = &i

//*q = p;  语法编译出错

//*q = *p;  error  q指向一个垃圾地址

q = p;  // error 可以读 q里面的垃圾地址,但是不能读*q的值,没有控制权限。

printf("%d\n", *q);

return 0;

}

————————————————————————–

/*

一个经典指针程序

*/

# include <stdio.h>

void huhuan(int i, int j);

void zhizhenhuhuan(int * a, int * b);

void huhuan3(int * a, int * b);

int main(void){

int a = 3;

int b = 5;

// huhuan(a, b);

// zhizhenhuhuan(&a, &b);

huhuan3(&a, &b);

printf("a = %d,b = %d\n", a, b);

return 0;

}

void huhuan(int a, int b){  //不能完成互换,a,b是形参,单独分配内存

int t;

t = a;

a = b;

b = t;

}

void zhizhenhuhuan(int * a, int * b){ //不能完成互换,互换了指针的指向

int * t;

t = a;

a = b;

b = t;

}

void huhuan3(int * a, int * b){  //可以完成互换,传递的是地址,交换的是地址指向的值

int t;

t = *a;

*a = *b;

*b = t;

}

—————————————————————————

*的含义

1.乘法   c = a*b;

2.定义指针变量 int * p;

3.取值运算符 *p

—————————————————————————

2.指针和数组的关系

 指针和一维数组

数组名

一维数组名是个指针常量

它存放的是数组第一个元素地址

—————————————————————-

int a[5];

int b[5];

a = b 是错误的  a,b都是常量

—————————————————————–

# include <stdio.h>

int main(void){

int a[5];

printf("%#x\n", &a[0]);

printf("%#x\n", a);

return 0;

}

输出结果:0x12ff6c

 0x12ff6c

—————————————————————-

下标和指针的关系

如果p是个指针变量,则

p[i]永远等价于 *(p+i)

确定一个一维数组需要两个参数

数组第一个元素的地址

数组的长度

————————————————————————-

//f函数可以输出任何一个一维数组的内容

# include <stdio.h>

void f(int * pArr, int len){

int i;

for (i=0; i<len; i++)

printf("%d  ", *(pArr+i));

printf("\n");

}

int main(void){

int a[5] = {1, 2, 3, 4, 5};

int b[6] = {-1, -2, -3, 4, 5, -6};

int c[100] = {1, 99, 22, 33};

f(a, 5); //确定一个数组:数组首地址和长度

f(b, 6);

f(c, 100);

return 0;

}

————————————————————————-

# include <stdio.h>

void f(int * pArr, int len){

pArr[3] = 88;  //pArr[3]等价于a[3]也等价于*(a+3)和*(pArr+3) 

  // *a==a[0]

}

int main(void){

int a[6] = {1, 2, 3, 4, 5, 6};

printf("%d\n", a[3]);

f(a, 6);  // a和pArr都指向数组的第一个元素

printf("%d\n", *(a+3));

return 0;

}

————————————————————————-

指针变量的运算

指针变量不能相加,不能相乘,也不能相除(这些运算没有意义)

如果两个指针变量指向的是同一块连续空间中得不同的存储单元

则这两个指针才可以相减(这样减才有意义)

——————————————————————————

# include <stdio.h>

int main(void){

int i = 5;

int j = 10;

int * p = &i;

int * q = &j;

//此时p和q不能相减

int a[5];

p = &a[2];

q = &a[4];

printf("相减的结果为:%d\n", p-q);

//此时p和q可以相减,相减的值指p和q单元相隔的个数

return 0;

}

结果为:相减的结果为:-2

———————————————————————————

一个指针变量到底占几个字节

预备知识:

sizeof(数据类型);或者 sizeof(变量名);

返回该数据类型所占的字节数

例如: sizeof(int) = 4 sizeof(char) = 1

假设p指向的char类型变量(1个字节)

假设p指向的int类型变量(4个字节)

假设p指向的double类型变量(8个字节)

p q r 本身所占的字节数是否一样

—————————————————————————

# include <stdio.h>

int main(void){

char ch = ‘A’;

int i = 90;

double x = 66.6;

char * p = &ch;

int *q = &i;

double *r = &x;

printf("%d  %d  %d\n", sizeof(p), sizeof(q), sizeof(r));

}

输出的结果:4  4  4

内存中一个字节为一个单元,所以double中有8个编号,

只用首字节的编号代表该变量的内存编号,用该变量类型

指明下面连续单元的个数

如: int * p;  int i = 5; p = &i;

p——>地址1101011(i的首地址)

int——-表示有四个字节

—————————————————————————-

 指针和二维数组

3.指针和函数的关系

4.指针和结构体的关系

5.多级指针

———————————————————————————-

# include <stdio.h>

int main(void){

int i = 10;

int * p = &i;

int ** q = &p;

int *** r = &q;

printf("i = %d\n", ***r);

return 0;

}

结果: i = 10

———————————————————————————–

专题:

动态内存分配

传统数组的缺点

1.数组的长度必须事先制定,且只能是常整数,不能是变量

 例如: int len = 5; int a[len];  //error

2.传统形式定义的数组,该数组的内存程序员无法手动释放

——————————————————————————

# include <stdio.h>

void f(void){

int a[5] = {1, 2, 3, 4, 5};

//这二十个字节的存储空间程序员无法手动编程释放它

//只能在本函数运行完毕时由系统自动释放

}

int main(void){

return 0;

}

——————————————————————————

3.数组的长度不能再函数运行的过程中动态的扩充或缩小

4.A函数定义的数组,在A函数运行期间可以被其它函数使用,

 但A函数运行完毕之后,A函数中的数组将无法在被其它函数使用

 静态数组不能跨函数使用

————————————————————————-

# include <stdio.h>

void g(int *pArr, int len){

pArr[2] = 88;

}

void f(void){

int a[5] = {1, 2, 3, 4, 5};  //f运行期间g();函数可以使用

g(a, 5);
//当f运行完毕数组a空间被释放

printf("%d\n", a[2]);

}

int main(void){

return 0;

}

————————————————————————–

为什么需要动态分配

动态数组很好的解决了传统数组的上面四个缺陷

动态内存分配的举例——动态数组的构造

—————————————————————————-

/*

2012年2月5日15:00:25

malloc 是 memory(内存) allocate(分配)的缩写

*/

# include <stdio.h.

# include <malloc.h>  //头文件

int main(void){

int i = 5; //静态分配了四个字节

int * p = (int *)malloc(4);  //把返回的地址强制转换为整形变量的地址

/*

1.要使用malloc函数,必须添加malloc.h这个头文件

2.malloc函数只有一个形参,并且是整型

3.4表示请求系统为本程序分配4个字节

4.malloc函数只能返回第一个字节的地址

5.上面一行代码分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节

6.p本身所占的内存是静态分配的,p所指向的内存是动态分配的

*/

*p = 5; //*p代表整形变量,只不过*p这个整形变量的内存分配方式和i分配不同

free(p);  //表示把p所指向的内存给释放掉

printf("同志们好!\n");

return 0;

}

—————————————————————————-

# include <stdio.h>

# include <malloc.h>

void f(int * q){

*q = 200;

free(q);  //把q指向的内存释放掉 和 free(p)等价

}

int main(void){

int * p = (int *)malloc(sizeof(int));

*p = 10;

printf("%d\n", *p);   //10

f(p);  //通过f()修改p指向内存的值

printf("%d\n", *p); //p指向的内存已经释放,所以会出错

return 0;

}

—————————————————————————-

/*

2012年2月5日15:37:36

动态一维数组示例

*/

# include <stdio.h>

# include <malloc.h>

int main(void){

int a[5]; //包含20字节

int i;

int len;

int *pArr;

printf("请输入你要存放的元素个数:");

scanf("%d", &len);

pArr = (int *)malloc(4*len);  //pArr指向前4个字节  类似于 int pArr[len];

// pArr+1 就指向第5到第8个字节

//动态的构造了一个int类型的一维数组,数组名 pArr

for (i=0; i<len; i++)  //对一维数组进行赋值

scanf("%d", &pArr[i]);

printf("一维数组的内容是:\n");

for(i=0; i<len; i++)

printf("%d\n", pArr[i]);

return 0;

}

输出结果:

请输入你要存放的元素个数:5

1 2 3 4 5

一维数组的内容是:

1

2

3

4

5

—————————————————————————-

静态内存和动态内存的比较

静态内存是由系统自动分配,由系统自动释放

静态内存是在栈中分配的

动态内存是由程序员手动分配,手动释放

动态内存是在堆分配的

跨函数使用内存的问题

—————————————————————————-

# include <stdio.h>

void f(int ** q){

int i = 5;

//*q 等价于p q 和**q都不等价于p

*q = &i;

}

int main(void){

int * p;

f(&p);  //访问完后释放内存

printf("%d\n", *p); //本语句语法没有问题, 但逻辑上有问题

//访问了不该访问的内存,f()函数结束后i的空间已经被释放

//静态变量,不能跨函数使用,当函数结束后变量不能被访问

return 0;

}

——————————————————————————

# include <stdio.h>

# include <malloc.h>

void f(int **q){

*q = (int*)malloc(sizeof(int)); //动态分配内存

//等价于 p = (int *)malloc(sizeof(int));

**q = 5;

}

int main(void){

int * p;

f(&p);

printf("%d\n", *p); //f()结束后,p指向的动态内存没有释放

return 0;

}

———————————————————————————-

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,985
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,501
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,345
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,128
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,763
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,839