数据结构与算法程序员C语言

慕课网-Linux C语言指针与内存-学习笔记

2017-08-21  本文已影响103人  天涯明月笙

Linux C语言指针与内存

工具与原理

C语言中指针的基本用法

#include <stdio.h>
void change(int a, int b)
{
    int tmp =a;
    a=b;
    b=tmp;
}

int main()
{
    int a=5;
    int b=3;
    change(a,b);
    printf("num a =%d\nnum b =%d\n",a,b);
    return 0;
}

上述代码无法实现a,b数值的交换。

改为指针实现代码如下:

#include <stdio.h>
void change(int *a, int *b)
{
    int tmp =*a;
    *a=*b;
    *b=tmp;
}

int main()
{
    int a=5;
    int b=3;
    change(&a,&b);
    printf("num a =%d\nnum b =%d\n",a,b);
    return 0;
}

3和5可以成功的交换。

需要将实参的地址传到子函数才能改变实参!(&a,&b)

C语言 int未初始化时,初值为随机
int变量未初始化的默认初值,和变量的类型有关。

gdb工具的使用

安装gdb工具:apt-get install gdb

gcc -g main.c -o main.out生成可调试版本。
gdb ./main.out

  • l:查看源代码

*a 取a这个地址的内容
&a 去a这个变量的地址

因为不知道一个指针指向的数据有多大, 所以需要在声明一个指针变量的时候需要明确的类型。

解析:只是传值,只是change的局部变量,是实参的备份。

解决:加个指针,取地址符,实现交换功能。

计算机中数据表示方法:

0x表示十六进制
1个16进制的数字,就可以表示4位二进制数字

内存管理

32根地址总线就有2的32次方个状态
内存分配
1byte = 8bit
1字节 = 8进制位

用户程序的内存空间从高到低又划分为:

高位内存空间分配给操作系统内核使用,低位内存空间分配给用户程序使用。
我们编写的函数在编译后存到磁盘,运行程序时,就把源代码编译后的二进制数据加载到内存空间中的代码段中。声明的全局变量或常量放置在数据段。每次调用新的函数,就将新的函数压入栈区。
64位系统中 只有前48位是给程序员使用的。 0x7fffffffffffffff ~ 0x0

变量与指针的本质

#include <stdio.h>

int global = 0;

int rect (int a,int b)
{
    static int count=0;
    count++;
    global++;
    int s=a*b;
    return s;
}

int quadrate(int a)
{
    static int count=0;
    count++;
    global++;
    int s = rect(a,a);
    return s;
}

int main()
{
    int a=3;
    int b=4;
    int *pa =&a;
    int *pb =&b;
    int *pglobal =&global;
    int (*pquadrate)(int a)= &quadrate;
    int s = quadrate(a);
    printf("%d\n",s);  
}

gdb调试命令:

变量的本质是什么?

指针的本质?
指针保存内存的地址。
a = 第五个柜子第二个抽屉。

操作系统对于内存的管理

栈先声明的地址大,后声明的地址小,与代码段数据段相反。
数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
32位系统指针占用4个字节, 也就是32个bit,64位系统占用64个bit,也就是8字节。

64位系统下,指针占8个字节,32位 4个字节。

编译器优化代码,把声明时不在一起的同一类型变量,放到一起(某种程度上修改了源码)

如 声明 int a ; float b ; int c; 编译后变量a的地址和c的地址是连在一起的.

CPU在编译的时候对栈内变量的存储地址进行优化,他会将类型相同的变量在连续地址中储存。

地址分配:代码,数据段是从下往上分配(先低地址,后高地址),栈是从上往下分配(先高地址,后低地址)

函数中静态变量,局部变量区别:
局部变量在栈(相对数据段而言的高地址)中,而静态变量在数据段(低地址)中.
所以在多次调用函数时,静态变量不会被重新,初始化. 或者这么说,静态变量的生存周期和数据段相同,局部变量生存时间受调用函数时,所属函数进栈出栈的影响而会重新初始化.

全局变量和静态变量都在数据段中,但静态变量是某个函数特有的.

函数指针与指针指向的数据访问

#include <stdio.h>

int global = 0;

int rect (int a,int b)
{
    static int count=0;
    count++;
    global++;
    int s=a*b;
    return s;
}

int quadrate(int a)
{
    static int count=0;
    count++;
    global++;
    int s = rect(a,a);
    return s;
}

int main()
{
    int a=3;
    int b=4;
    int *pa =&a;
    int *pb =&b;
    int *pglobal =&global;
    int (*pquadrate)(int a)= &quadrate;
    int s = quadrate(a);
    printf("%d\n",s);  
}

数组申明的内存排列

(gdb)p *0x7fffffffcc6c
$15 = 1

(gdb) p *0x7fffffffcc70
$17 = 10

(gdb) p *0x7fffffffcc74
$18 =100

可以看出数组是按顺序放置元素的。

指针运算。

指针偏移运算。
p +=3;
把指针往下移三格-整型移动12个字节
*p =101;
将p指针所指向的值修改为101
p =&a;
让p再次指向a的地址
p[4] = 101
把指针往下移三格-整型移动12个字节
P[4]不是p往下面移动了4个位置,而是从p开始的地址往后移动4个位置取值,p指向的地址还是不变的

数组其实就是个指针常量,指针是指针变量,常量就是不可更改的

int array[2];
int *pa =array;
pa[0]=1;
pa[1]=10;
pa[2]=100;

字符数组和指针字符串

int a;
scanf("%d",&a);

int main()
{
    char str[]="hello";
    char *str2="world";
    char str3[10];
    printf("input the value\n");
    scanf("%s",str3);
    printf("str is %s\n",str); 
    printf("str2 is %s\n",str2);
    printf("str3 is %s\n",str3);
}

因为str3是一个字符数组,

gdb的x命令,可以打印地址中的值

scanf可以将输入存入str或str3,但是不能存入str2
堆和栈内存里才可以写入(预留空间才可写入)
而str2是一个代码段变量。不允许写入。

字符串数组的深入理解

#include <stdio.h>

int main()
{
    char str[]="hello";
    char *str2="world";
    char str3[10];
    printf("input the value\n");
    str[3]='\0';
    scanf("%s",str3);
    printf("str is %s\n",str); 
    printf("str2 is %s\n",str2);
    printf("str3 is %s\n",str3);
}

只会打印出hel,因为prinf打印以/0为结束。

char str1[] = "Hello";
char str3[5];
str1 = "HelloWorld";

很有意思 :

String类型输出遇到/0 结束
char类型输出遇到/0 继续输出.

指针变量char *str2 = "hello",用scanf 向str2中输入字符串出错,其实也可以这么理解,指针str2只是指向一个地址,从这个地址开始写入"hello",没有指定内存长度,没有空间去容纳字符串。内存溢出!这个与char str[] = "hello"不同,str已经有了6个字节的内存空间,

上一篇 下一篇

猜你喜欢

热点阅读