`
universsky
  • 浏览: 96888 次
文章分类
社区版块
存档分类
最新评论

C语言精华:指针示例精讲----gdb, 汇编内存地址

 
阅读更多
/**
指针是一个用来指示一个内存地址的计算机语言的变量或中央处理器(CPU)中寄存器。

指针pointer 是一个变量的地址
指针变量是一个变量其值是另一个变量的地址
任何变量都在计算机内存中占有一块内存区域, 变量的值就存放在这块内存区域之中, 寄存器变
量不在内存中而是在CPU 的寄存器中 

有两个运算符可以引用指针变量
1   & 取地址运算符如 pointer_1 = &i;
2   * 指针运算符用于访问指针变量所指向的变量;

指针一般出现在比较近机器语言的语言,如汇编语言或C语言。面向对象的语言如Java一般避免用指针。指针一般指向一个函数或一个变量。在使用一个指针时,一个程序既可以直接使用这个指针所储存的内存地址,又可以使用这个地址里储存的函数的值。
在计算机语言中,由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。

指针与C语言
大家都认为,c语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。同时,这种说法也让很多人产生误解,似乎只有C语言的指针才能算指针。basic不支持指针,在此不论。其实,pascal语言本身也是支持指针的。从最初的pascal发展至今的object pascal,可以说在指针运用上,丝毫不会逊色于c语言的指针。

内存分配表
计算机中的内存都是编址的,就像你家的地址一样。在程序编译或者运行的时候,系统(可以不关心具体是什么,可能是编译器,也可能是操作系统)开辟了一张表。每遇到一次声明语句(包括函数的传入参数的声明)都会开辟一个内存空间,并在表中增加一行纪录。记载着一些对应关系。(

概念
在信息工程中指针是一个用来指示一个内存地址的计算机语言的变量或中央处理器(CPU)中寄存器(Register)。指针一般出现在比较近机器语言的语言,如汇编语言或C语言。面向对象的语言如Java一般避免用指针。指针一般指向一个函数或一个变量。在使用一个指针时,一个程序既可以直接使用这个指针所储存的内存地址,又可以使用这个地址里储存的函数的值。
指针还可能是钟表的指针,中医学的指针法,或者指针网等。
在计算机语言中,由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。[1]编辑本段信息工程
指针与C语言
大家都认为,c语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。同时,这种说法也让很多人产生误解,似乎只有C语言的指针才能算指针。basic不支持指针,在此不论。其实,pascal语言本身也是支持指针的。从最初的pascal发展至今的object pascal,可以说在指针运用上,丝毫不会逊色于c语言的指针。
内存分配表
计算机中的内存都是编址的,就像你家的地址一样。在程序编译或者运行的时候,系统(可以不关心具体是什么,可能是编译器,也可能是操作系统)开辟了一张表。每遇到一次声明语句(包括函数的传入参数的声明)都会开辟一个内存空间,并在表中增加一行纪录。记载着一些对应关系。(如图1所示)

  图1
----------------------------------------------------
Declaration | ID Name Address Length
----------------------------------------------------
int nP; | 1 nP 2000 2B
char myChar; | 2 myChar 2002 1B
int *myPointer; | 3 myPointer 2003 2B
char *myPointer2; | 4 myPointer2 2005 2B
----------------------------------------------------
指针是一个整数
指针,是一个无符号整数(unsigned int),它是一个以当前系统寻址范围为取值范围的整数。32位系统下寻址能力(地址空间)是4G-byte(0~2^32-1)二进制表示长度为32bit(也就是4B)。 int类型也正好如此取值。
例证(一)
例证就是程序1得到的答案和程序2的答案一致。(不同机器可能需要调整一下pT的取值。)
----------------------------------------------------
程序1
#include <stdio.h>
main()
{
char *pT;
char t='h';
pT=&t;
putchar(*pT);
}
----------------------------------------------------
程序2
#include <stdio.h>
main()
{
char *pT;
char t='h';
pT=(char *)1245048;
putchar(*pT);
}
----------------------------------------------------
加上(char *)是因为毕竟int 和char *不是一回事,需要强制转换,否则会有个警告。因为char *声明过的类型,一次访问1个sizeof(char)长度,double *声明过的类型,一次访问1个sizeof(double)长度。
在汇编里int 类型和指针就是一回事了。因为不论是整数还是指针,执行自增的时候,都是其值加一。如果上文声明char *pT;,汇编语言中pT自增之后值为1245049,可是C语言中pT++之后pT值为1245049。如果32 位系统中, s 上文声明int *pT;,汇编语言中pT 自增之后值为1245049,可是C 语言中pT++之后pT值为1245052。
为什么DOS下面的Turbo C,和Windows下VC的int类型不一样长。因为DOS是16位的,Windows是32位的,可以预见,在64位Windows 中编译,上文声明int *pT;,pT++之后pT值为1245056。
例证(二)
对于复杂的结构,如C语言的结构体(汇编语言对应为Record类型)按顺序分配空间。(如图2所示)

  图2
----------------------------------------------------
int a[20];
----------------------------------------------------
typedef struct st
{
double val;
char c;
struct st *next;
} pst;
----------------------------------------------------
pst pT[10];
----------------------------------------------------
在32 位系统下,内存里面做如下分配(单位:H,16 进制);(如图3所示)

  图3
----------------------------------------------------
变量2000 2001 2002 2003 2004 2005 2006 … 204C 204D 204E 204F
地址 a[0] a[1] … a[19]
----------------------------------------------------
变量 2050 2051 … 2057 2058 2059 205A 205B 205C 205D 205E 205F
地址 pst.val pst.c pst.next 无效 无效 无效
----------------------------------------------------
这就说明了为什么sizeof(pst)=16而不是8。编译器把结构体的大小规定为结构体成员中大小最大的那个类型的整数倍。
至于pT的存储,可以依例推得。总长为160,此不赘述。
有个问题,如果执行pT++,答案是什么?是自增16,还是160?别忘了,pT 是常量,不能加减。
所以,我们就可以声明:
----------------------------------------------------
typedef struct BinTree
{
int value;
struct BinTree *LeftChild;
struct BinTree *RightChild;
} BTree;
----------------------------------------------------
用一个整数,代表一棵树的结点。把它赋给某个结点的LeftChild/RightChild 值,就形成了上下级关系。只要无法找到一个路径,使得A->LC/RC->LC/RC...->LC/RC==A,这就构成了一棵二叉树。反之就成了图。
按值传递
C中函数调用是按值传递的,传入参数在子函数中只是一个初值相等的副本,无法对传入参数作任何改动。但实际编程中,经常要改动传入参数的值。这一点我们可以用传入参数的地址而不是原参数本身,当对传入参数(地址)取(*)运算时,就可以直接在内存中修改,从而改动原想作为传入参数的参数值。
编程参数值
----------------------------------------------------
#include <stdio.h>
void inc(int *val)
{
(*val)++;
}
main()
{
int a=3;
inc(&a);
printf("%d" , a);
}
----------------------------------------------------
在执行inc(&a);时,系统在内存分配表里增加了一行“inc 中的val”,其地址为新地址,值为&a。操作*val,即是在操作a 了。
*和&运算
(*p)操作是这样一种运算,返回p 的值作为地址的那个空间的取值。(&p)则是这样一种运算,返回当时声明p 时开辟的地址。显然可以用赋值语句对内存地址赋值。




*/



#include<stdio.h>
int main()
{
	int a=3;
	int b=5;
	int* pa=&a;
	int* pb=&b;

	printf("a=%d,b=%d\n&a=%d,&b=%d\npa=%d,pb=%d\n&pa=%d,&pb=%d\n",a,b,&a,&b,pa,pb,&pa,&pb);

    printf("\n*pa=%d, *pb=%d\n",*pa,*pb);
    
	return 0;
}




/**
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=-811048276,&b=-811048280
pa=-811048276,pb=-811048280
&pa=-811048288,&pb=-811048296

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=164267212,&b=164267208
pa=164267212,pb=164267208
&pa=164267200,&pb=164267192

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=505031116,&b=505031112
pa=505031116,pb=505031112
&pa=505031104,&pb=505031096

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=-618657252,&b=-618657256
pa=-618657252,pb=-618657256
&pa=-618657264,&pb=-618657272

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=1994135004,&b=1994135000
pa=1994135004,pb=1994135000
&pa=1994134992,&pb=1994134984

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=529256668,&b=529256664
pa=529256668,pb=529256664
&pa=529256656,&pb=529256648

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=-695279348,&b=-695279352
pa=-695279348,pb=-695279352
&pa=-695279360,&pb=-695279368

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=-727821812,&b=-727821816
pa=-727821812,pb=-727821816
&pa=-727821824,&pb=-727821832

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=1643161388,&b=1643161384
pa=1643161388,pb=1643161384
&pa=1643161376,&pb=1643161368

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=-1116086340,&b=-1116086344
pa=-1116086340,pb=-1116086344
&pa=-1116086352,&pb=-1116086360

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=-386721972,&b=-386721976
pa=-386721972,pb=-386721976
&pa=-386721984,&pb=-386721992

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=-16000804,&b=-16000808
pa=-16000804,pb=-16000808
&pa=-16000816,&pb=-16000824

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=146760732,&b=146760728
pa=146760732,pb=146760728
&pa=146760720,&pb=146760712

*pa=3, *pb=5
OCS101:~/cpl # ./a.out 
a=3,b=5
&a=1544526108,&b=1544526104
pa=1544526108,pb=1544526104
&pa=1544526096,&pb=1544526088

*pa=3, *pb=5
*/


#include "stdio.h"

int swap(int *a, int *b)
{
	int temp;
	
	temp = *a;
	*a = *b;
	*b = temp;
}

int main()
{
	int a,b;
	int *pa,*pb;

	a = 3;
	pa = &a;

	b = 5;
	pb = &b;

	printf("a=%d,b=%d\npa=%d,pb=%d\n&a=%d,&b=%d\n*pa=%d,*pb=%d\n",a,b,pa,pb,&a,&b,*pa,*pb);
	
	if(a < b) swap(pa,pb);

	printf("After swap:\na=%d,b=%d\n",a,b);

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

	printf("*pa=%d,*pb=%d\na=%d,b=%d\n",*pa,*pb,a,b);
}

/**变量地址不变,变量存储的值变化:

OCS101:~/cpl # ./a.out 
a=3,b=5
pa=-837469844,pb=-837469848
&a=-837469844,&b=-837469848
*pa=3,*pb=5
After swap:
a=5,b=3
pa=-837469844,pb=-837469848
&a=-837469844,&b=-837469848
*pa=5,*pb=3
a=5,b=3
*/


#include "stdio.h"

int main()
{
	int a,b;
	printf("======================\na=%d,b=%d\n&a=%d,&b=%d\n",a,b,&a,&b);
	
	a=3;
	b=5;

	printf("After set value of a,b: \na=%d,b=%d\n&a=%d,&b=%d\n",a,b,&a,&b);

	int temp;
        printf("temp=%d,&temp=%d\n",temp,&temp);

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

	a=b;

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

	b=temp;

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

	
	/**
	OCS101:~/cpl # gcc testNormalSwap.c
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=-1465855444,&b=-1465855448
After set value of a,b: 
a=3,b=5
&a=-1465855444,&b=-1465855448
temp=32767,&temp=-1465855452
temp=3,&temp=-1465855452
a=5,&a=-1465855444
b=3,&b=-1465855448
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=57986924,&b=57986920
After set value of a,b: 
a=3,b=5
&a=57986924,&b=57986920
temp=32767,&temp=57986916
temp=3,&temp=57986916
a=5,&a=57986924
b=3,&b=57986920
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=560062012,&b=560062008
After set value of a,b: 
a=3,b=5
&a=560062012,&b=560062008
temp=32767,&temp=560062004
temp=3,&temp=560062004
a=5,&a=560062012
b=3,&b=560062008
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=372484956,&b=372484952
After set value of a,b: 
a=3,b=5
&a=372484956,&b=372484952
temp=32767,&temp=372484948
temp=3,&temp=372484948
a=5,&a=372484956
b=3,&b=372484952
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=-1057179668,&b=-1057179672
After set value of a,b: 
a=3,b=5
&a=-1057179668,&b=-1057179672
temp=32767,&temp=-1057179676
temp=3,&temp=-1057179676
a=5,&a=-1057179668
b=3,&b=-1057179672
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=1450659740,&b=1450659736
After set value of a,b: 
a=3,b=5
&a=1450659740,&b=1450659736
temp=32767,&temp=1450659732
temp=3,&temp=1450659732
a=5,&a=1450659740
b=3,&b=1450659736
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=1965349484,&b=1965349480
After set value of a,b: 
a=3,b=5
&a=1965349484,&b=1965349480
temp=32767,&temp=1965349476
temp=3,&temp=1965349476
a=5,&a=1965349484
b=3,&b=1965349480
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=-1143257652,&b=-1143257656
After set value of a,b: 
a=3,b=5
&a=-1143257652,&b=-1143257656
temp=32767,&temp=-1143257660
temp=3,&temp=-1143257660
a=5,&a=-1143257652
b=3,&b=-1143257656
OCS101:~/cpl # ./a.out 
======================
a=0,b=0
&a=-1518030740,&b=-1518030744
After set value of a,b: 
a=3,b=5
&a=-1518030740,&b=-1518030744
temp=32767,&temp=-1518030748
temp=3,&temp=-1518030748
a=5,&a=-1518030740
b=3,&b=-1518030744
	*/


分享到:
评论

相关推荐

    C语言案例3:欢快小跑车-配套资源.zip

    1. 变量和数据类型:C语言中的基本数据类型如int、char、float、double等,以及如何声明和初始化变量,是编程的基石。在小跑车案例中,可能涉及速度、位置等变量的定义和操作。 2. 控制结构:包括条件语句(if-else...

    汇编:C语言.zip

    - 学习C语言中的指针时,汇编视角有助于理解内存地址和数据访问的实际操作。 - 函数调用的过程,包括参数传递、栈帧的建立和销毁,在汇编中有着直观的表示。 4. **C语言的关键概念与汇编对应**: - 变量:在汇编...

    嵌入式Linux基础教程 英文版 pdf Embedded Linux Primer: A Practical Real-World Approach

    - **实践导向**:本书采用实际案例和示例来阐述理论知识,使读者能够快速掌握核心技能。 - **全面覆盖**:涵盖了从基础知识到高级技巧的多个层面,满足不同层次读者的需求。 - **深入剖析**:对于每个主题都进行了...

    C语言程序设计实训--源代码

    - 指针变量:存储内存地址的变量,可以用于动态内存管理、高效数据操作和实现高级数据结构。 - 指针运算:指针加减运算,用于遍历数组或结构体。 - 函数指针:可以指向函数的指针,常用于回调函数和函数工厂模式...

    C语言入门编程工具:Dev-Cpp 5.11 TDM-GCC 4.9.2 Setup

    Dev-C++(或者叫做Dev-Cpp)是Windows环境下的一个轻量级C/C++集成...Dev-C++集合了功能强大的源码编辑器、MingW64/TDM-GCC编译器、GDB调试器和AStyle格式整理器等众多自由软件,适合于在教学中供C/C++语言初学者使用。

    docker-gdal-with-FileGDB:具有FileGDB写入支持的GDAL

    具有FileGDB(ESRI)的GDAL写入支持这是一个使用ESRI FileGDB API源文件编译gdal二进制文件的docker映像,它支持写入FileGDB输出数据。 当前,这仅在Docker容器中创建64位版本。 基于Ubuntu 14.04。致谢该存储库无耻...

    gdb-7.6.1-1-mingw32-bin.tar.rar

    安装MinGw报错无法下载gdb-7.6.1-1-mingw32-bin.tar.lzma 。可以下载这个文件,把其中解压出来的gdb.exe放在MinGw/bin目录下即可。注意解压lzma文件需要使用特殊软件,如:“7-zip”。

    C语言经典经典例子---快速成为C中高手

    特别地,指针是C语言的一大特色,通过指针可以高效地处理内存和数据结构,比如动态内存分配、数组操作和链表等复杂数据结构的实现。 文件名"c"可能代表了一系列的C语言源代码文件,每个文件对应一个或多个具体的...

    xtensa-esp-elf-gdb-11.2_20220823-aarch64-linux-gnu.tar.gz

    1. **安装**:首先解压`xtensa-esp-elf-gdb-11.2_20220823-aarch64-linux-gnu.tar.gz`,获取GDB工具。 2. **配置环境**:将GDB添加到PATH环境变量,确保可以在命令行中直接调用。 3. **连接设备**:通过`gdbserver...

    gdb-multiarch 14.2 最新windows 64位版本,支持python3

    GDB-Multiarch是其一个扩展版本,它增加了对多种架构的支持,允许开发者在不同架构之间进行跨平台调试。在本案例中,我们讨论的是GDB-Multiarch 14.2的最新Windows 64位版本,它已经更新为支持Python 3。 1. **GDB-...

    C语言编程宝典----细节经验

    4. **指针**:C语言的指针是其强大之处,它们允许直接操作内存。理解指针的声明、解引用、指向数组和函数,以及动态内存分配(malloc、calloc、realloc、free)是高级C编程的必备技能。 5. **数组与字符串**:数组...

    Linux下C语言应用编程(作者-杨铸)配套教学ppt

    - `11-Linux下C语言编程环境.ppt`可能涵盖了如何在Linux上设置开发环境,包括安装GCC编译器,使用Makefile进行项目管理,以及调试工具GDB的使用。 2. **文件IO编程**: - `12-文件IO编程.ppt`会讲解Linux下的文件...

    C语言集训营1期c-language-training-camp-1-master.zip

    - 变量与数据类型:C语言提供了多种基本数据类型,如int、char、float和double等,以及结构体、联合体和枚举等复杂数据类型。 - 常量与变量:理解常量的不可变性以及变量的可变性,掌握如何声明和初始化它们。 - ...

    C语言经典笔试面试题--自理

    C语言经典笔试面试题涵盖了C语言的基础概念、多线程与进程的理解以及程序编译过程等多个方面。以下是对这些知识点的详细解析: 1. Static 关键字: - 在函数内部,static 变量的值在函数每次调用之间保持不变,它...

    gdb-peda-pwndbg-gef:自动为gdb安装Peda + pwndbg + GEF插件的脚本

    gdb-peda gdb-peda-intel gdb-peda-arm gdb-pwndbg gdb-gef 有关更多信息,请阅读相关博客文章: 安装 cd ~ && git clone https://github.com/soaringk/gdb-peda-pwndbg-gef.git cd ~/gdb-peda-pwndbg-gef ./...

    C语言二级历年真题-----改错题

    在准备全国计算机二级考试,尤其是针对C语言部分时,考生们常常会遇到各种类型的题目,如选择题、程序填空题以及改错题。这里我们主要聚焦于“改错题”这一部分,它是考察考生对C语言语法和编程逻辑理解的重要方式。...

    Linux期末作业-关于Linux-C语言gcc编译gdb调试、shell脚本编程

    在Linux操作系统中,C语言是核心开发语言之一,而GCC(GNU Compiler Collection)是用于编译C语言的主要工具。GCC不仅支持C,还支持C++、Fortran等其他编程语言。GDB(GNU Debugger)则是C语言开发过程中的重要调试...

    aarch64-linux-gnu-gdb

    官网下载最新的aarch64 gdb调试工具压缩包,有需要的可以看下

Global site tag (gtag.js) - Google Analytics