C语言中的数组和指针汇编代码分析实例

今天看《程序员面试宝典》时偶然看到讲数组和指针的存取效率,闲着无聊,就自己写了段小代码,简单分析一下C语言背后的汇编,可能很多人只注重C语言,但在实际应用当中,当出现问题时,有时候还是通过分析汇编代码能够解决问题。本文只是为初学者,大牛可以飘过~

C源代码如下:

代码如下:

#include "stdafx.h"
int main(int argc, char* argv[])
{
       char a=1;
       char c[] = "1234567890";
       char *p = "1234567890";
       a = c[1];
       a = p[1];
       return 0;
}

在VC6.0下查看汇编代码步骤:
在main函数中靠前的部分随便一行F9设置断点->编译->F5 在调试界面中右键->Go to disassembly

Debug汇编代码(已加注释):

代码如下:

4:    #include "stdafx.h"
5:
6:    int main(int argc, char* argv[])
7:    {
00401010   push        ebp    
00401011   mov         ebp,esp      ;保存栈帧
00401013   sub         esp,54h        ;抬高栈顶
00401016   push        ebx
00401017   push        esi
00401018   push        edi                     ;压入程序中用到的寄存器,以便恢复
00401019   lea         edi,[ebp-54h]            
0040101C   mov         ecx,15h
00401021   mov         eax,0CCCCCCCCh
00401026   rep stos    dword ptr [edi]    ;栈顶与栈帧之间的数据填充为0xcc,相当于汇编中的int 3,这是因为debug模式下把Stack上的变量都初始化为0xcc,检查未初始化的问题
8:        char a=1;
00401028   mov         byte ptr [ebp-4],1      ;ebp-4是为变量a分配的空间地址
9:        char c[] = "1234567890";
0040102C   mov         eax,[string "1234567890" (0042201c)]
00401031   mov         dword ptr [ebp-10h],eax   ;“1234567890”是字符串常量,存储在地址0042201c处,ebp-10是为数组C分配的空间的首地址,空间大小从ebp-0x10到ebp-0x04,共12个字节。本句中先把“1234”这4个字节拷贝到数组C中
00401034   mov         ecx,dword ptr [string "1234567890" 4 (00422020)]
0040103A   mov         dword ptr [ebp-0Ch],ecx  ;作用同上,把“5678”这4个字节拷贝到数组C中
0040103D   mov         dx,word ptr [string "1234567890" 8 (00422024)]
00401044   mov         word ptr [ebp-8],dx   ;作用同上,把“90”这2个字节拷贝到C中
00401048   mov         al,[string "1234567890" 0Ah (00422026)]
0040104D   mov         byte ptr [ebp-6],al    ;这个大家都熟,不要忘了\0
10:       char *p = "1234567890";
00401050   mov         dword ptr [ebp-14h],offset string "1234567890" (0042201c) ;ebp-0x14是为指针p分配的空间地址,大小是4个字节,地址中的值是字符串“1234567890”的首地址
11:       a = c[1];
00401057   mov         cl,byte ptr [ebp-0Fh]  ;这里是重点,因为数组C在栈上连续存储,很容易根据ebp找到第其中一个字符的地址,并取值,赋给cl
0040105A   mov         byte ptr [ebp-4],cl     ;完成赋值
12:       a = p[1];
0040105D   mov         edx,dword ptr [ebp-14h]  ;这里与上面就有区别,因为根据ebp只知道指针p的值,先得到p的值,即先得到一个指针
00401060   mov         al,byte ptr [edx 1]    ;根据得到的指针间接的找到字符串中的一个字符
00401063   mov         byte ptr [ebp-4],al
13:       return 0;
00401066   xor         eax,eax         ;eax清0,作为main函数的返回值
14:   }
00401068   pop         edi
00401069   pop         esi
0040106A   pop         ebx
0040106B   mov         esp,ebp
0040106D   pop         ebp     ;恢复ebp
0040106E   ret

好了,可以看到,用数组访问元素,只需2步,而用指针时要3步。可见数组和指针并不相同,有时候大家都认为可以把数组的名称看成一个指针,这种想法有时候没错,但有时候却会出错。我再举一个简单的例子,而下面的这个例子可能是大家在开发过程中经常会碰到的问题。

在文件test.cpp中:

代码如下:

#include "stdafx.h"
#include "inc.h"
extern char chTest[10];
int main(int argc, char* argv[])
{
       printf("chTest=%s\n", chTest);
       return 0;
}

上面有个extern声明,表明chTest数组是在外部文件中定义过的。chTest定义在inc.h中:

代码如下:

char chTest[10]="123456789";

上述的程序,经编译后,可以成功运行。但如果把红色的代码改成如下:

代码如下:

extern char *chTest;

这时,程序在编译的时候就会通不过,提示的错误信息是:redefinition; different types of indirection,但这时候并没有错误出现在哪一行的说明,如果是在开发一个大型工程,那么就不容易定位问题出在哪个地方。造成上述错误的原因我想大家都明白了,就是因为当chTest作为一个指针被引用时,其元素访问的方式与数组是不同的,就算程序能编译通过,在运行时,也是会出现错误。

好了,上述的内容都是个人有感而发,是些简单零碎的东西,笑纳。如有哪些地方说的不合适,而望指正!

(0)

相关推荐

  • 举例理解C语言二维数组的指针指向问题

    之前对数组的概念一直没有理解透彻,只觉得数组名就是个常量指针而已,用法和基本的指针差不多.所以当我尝试用二级指针去访问二维数组时,就经常会出错.下面就是刚开始写的一个错误的程序: #include <stdio.h> int main() { int iArray[2][3] = {{1,2,3},{4,5,6}}; int **pArray = NULL; pArray = iArray; printf("array[0][0] = %d\n", pArray[0][0]

  • 简单分析C语言中指针数组与数组指针的区别

    首先来分别看一下,指针数组的一个小例子: #include <stdio.h> #include <string.h> int lookup_keyword(const char*key, const char* table[], const int size) { int ret = -1; int i = 0; for(i=0; i<size; i++) { if (strcmp(key, table[i]) == 0) { ret = i; break; } } ret

  • 直观理解C语言中指向一位数组与二维数组的指针

    一维数组和指针: 对于一位数组和指针是很好理解的: 一维数组名: 对于这样的一维数组:int a[5];  a作为数组名就是我们数组的首地址, a是一个地址常量 . 首先说说常量和变量的关系, 对于变量来说, 用箱子去比喻再好不过了, 声明一个变量就声明一个箱子,比如我们开辟出一个苹果类型的箱子, 给这个变量赋值就是把盛放苹果的箱子中放入一个实实在在的苹果, 这就是变量的赋值.  而对于数组来说, 就是一组类型相同的箱子中,一组苹果箱子, 可以放入不同的苹果. 一维数组空间: 变量被声明后, 我

  • C语言中二维数组指针的简要说明

    C语言中,指针是一个复杂但又灵活多变的知识点,我们知道,在一维数组中,对于一个数组a[],*a,a,&a,都表示a的首地址,但如果与二维数组混合使用,就显得更为复杂了.例如对于一个二维数组 a[2][4]={{1,2.3},{4,5,6}} a+i,&a[i],*(a+i),a[i], 这四个表达式到底表示什么呢? 先告诉答案吧,其实这几个表达式都是指向同一个地址的,也许你会很诧异,也会很疑惑,怎么会是这样呢!!事实证明就是这样的, C语言中,指针是一个复杂但又灵活多变的知识点,我们知道,

  • C语言数组指针的小例子

    1.功能:输入6个学生的5门课程成绩,计算出每个学生的平均分和每门课程的平均分.2.C语言实现代码:(其实就是用二维数组来实现的,二维数组的引用传递使用数组指针来完成) 复制代码 代码如下: #include <stdio.h>#define STUDENT 5#define SCORE 6void input_array(float (*score)[STUDENT]);void avg_score(float (*score)[STUDENT]);void avg_course(float

  • C语言安全之数组长度与指针实例解析

    1.C语言编码需要保证变长数组的长度参数位于合法范围之内 例如以下代码: void func(size_t s) { int vla[s]; /*...*/ } /*...*/ func(size); /*...*/ 解决方案如下: enum {MAX_ARRAY = 1024}; void func(size_t s) { if(s < MAX_ARRAY && s != 0) { int vla[s]; /*...*/ } else { //错误处理 } } /*...*/ fu

  • C语言中的数组和指针汇编代码分析实例

    今天看<程序员面试宝典>时偶然看到讲数组和指针的存取效率,闲着无聊,就自己写了段小代码,简单分析一下C语言背后的汇编,可能很多人只注重C语言,但在实际应用当中,当出现问题时,有时候还是通过分析汇编代码能够解决问题.本文只是为初学者,大牛可以飘过~ C源代码如下: 复制代码 代码如下: #include "stdafx.h" int main(int argc, char* argv[]) {        char a=1;        char c[] = "

  • C语言中 值传递和指针传递实例详解

    C语言中 值传递和指针传递实例详解 在C语言中,函数的参数和返回值的传递方式有两种:值传递和指针传递. 值传递和指针传递初学者总会有一种朦胧的感觉,所以建议把指针传递的概念摸透,才能熟练应用. 值传递示例:x其实是n的一份临时拷贝,所以并不会改变n的值. #include <stdio.h> #include <windows.h> void Fun(int x) { x = 1; } int main() { int n = 2; Fun(n); printf("%d\

  • C语言中全局数组和局部数组的问题

    今天同学遇到一个在C语言中全局数组和局部数组的问题,卡了许久,我也没有第一时间看出问题,现在把问题梳理一下,并给出解决方案. 问题描述: 在全局声明的数组与在局部声明的数组有着不同的效果. 首先来看一个程序: 复制代码 代码如下: #include <stdio.h> #include <stdlib.h> #define MAX 10 char a[MAX]; int main() { int i; char b[MAX]; char *c=(char *)malloc(MAX

  • go语言中值类型和指针类型的深入理解

    golang这个语言用起来和java. c#之类语言差不多,和c/c++差别比较大,有自动管理内存机制,省心省力. 然而,如果写golang真的按写java的习惯去写,也容易出问题,因为golang中有指针的概念,虽然这个指针是c/c++的自动化版本,但是却也有指针的特征,如果不熟悉其中原理,写出来的程序虽然不至于有运行BUG,性能却不友好. 因此,不能完全以写java的思路去写golang,一定要注意其中差别. 我们知道,在java之中,除了基本类型之外,所有的变量类型都是引用类型,你可以随意

  • Python中eval带来的潜在风险代码分析

    0x00 前言 eval是Python用于执行python表达式的一个内置函数,使用eval,可以很方便的将字符串动态执行.比如下列代码: >>> eval("1+2") >>> eval("[x for x in range(10)]") [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 当内存中的内置模块含有os的话,eval同样可以做到命令执行: >>> import os >>&g

  • python中delattr删除对象方法的代码分析

    最近我们针对对象属性这块,介绍了不少关于测试属性的方法.在进行一系列测试后,我们发现这个属性并不需要,这时候就要用到删除的功能.在python中可以选择delattr函数删除对象的属性,基于它的删除功能,是否能扩展到删除的对象的方法上,在我们对delattr函数进行全面了解后,展开实例的测试. 1.说明 函数作用用来删除指定对象的指定名称的属性,和setattr函数作用相反. 不能删除对象的方法. 2.参数 object -- 对象. name -- 必须是对象的属性. 3.返回值 无. 4.实

  • 简单总结C语言中各种类型的指针的概念

    C语言中有很多关于指针的使用,指针也是C语言的灵魂所在,而且C语言中也有很多有关指针的概念,这里学习并总结了一些知道的概念.   常量指针: 首先它是一个指针,常量只是用来修饰指针的定语.其定义如下: char const * cp; char a='a'; 如何识别呢?根据右结合优先,先是*优先,所以这个cp变量是一个指针,然后是const修饰*,所以这是一个常量指针.即指向常量的指针. cp=&a; //正常语法 *cp=a; //错误语法,因为其指向的值是一个常量 指针常量: 首先它是一个

  • 简单聊一聊Go语言中的数组和切片

    目录 1. 数组 2. 切片(Slice) append 函数 总结 1. 数组 数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成.因为数组的长度是固定的,因此在 Go 语言中很少直接使用数组.和数组对应的类型是 Slice(切片),它是可以增长和收缩的动态序列,slice 功能也更灵活. 数组的每个元素可以通过索引下标来访问,索引下标的范围是从 0 开始到数组长度减 1 的位置.内置的 len 函数将返回数组中元素的个数. var a [3]int // arra

  • C语言中的初阶指针详解

    目录 1.指针是什么 2.指针和指针类型 3.野指针 3.1野指针成因 3.2如何规避野指针 4.指针的运算 4.1指针±整数 4.2指针-指针 4.3指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 ​ 总结 1.指针是什么 ​ 初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元. 地址指向了一个确定的内存空间,所以地址形象的被称为指针. int main() { int a = 10; int* pa = &a; return 0; } //pa是

随机推荐