C语言指针与数组的定义与声明易错分析

部分摘自《C语言深度解剖》

1.定义为数组,声明为指针

在文件1中定义:

char a[100];

在文件2中声明:

extern char *a;  //这样是错误的

这里的extern告诉编译器a这个名字已经在别的文件中被定义了,下面的代码使用的a是在别的文件中定义的。编译器是按文件分别编译的,当a被声明为char* a时,编译器理所当然的认为a是一个指针变量,在32位系统下占用4个byte,这4个byte存放的是地址,地址指向的空间存储的是char类型数据。

程序会返回SIGSEGV。

2.定义为指针,声明为数组

文件1:char* p="abcdefg";

文件2:extern char p[];  //这样也是错误的

在文件1中,编译器分配4个byte空间,并命名为p。同时p里面保存了字符串常量“abcdefg”的首字符地址。这个字符串常量本身保存在内存的静态区,其内容不可修改。在文件2中,编译器认为p是一个数组,其大小为4byte,数组保存的是char类型数据。

总结:代码在一个地方定义为指针,在别的地方也只能声明为指针;同理数组。

指针数组与数组指针

指针数组:首先它是一个数组,数组的元素都是指针。

数组指针:首先它是一个指针,指针指向一个数组。

[]比*优先级高

A) int *p1[10]; => (int *)p1[10]; 即它首先是一个数组,数组的元素都是int*;

B) int (*p2)[10];  首先它是一个指针,指针指向一个包含10个元素的数组;

A:指针数组 B:数组指针

main()
{
    char a[5]={‘A‘,‘B‘,‘C‘,‘D‘};
    char (*p3)[5]=&a;	//数组指针
    char (*p4)[5]=a;      	//会产生警告:从不兼容的指针类型初始化
    char (*p5)[3]=&a;	//waring
    char (*p6)[3]=a;	        //waring
    char (*p7)[10]=&a;	//waring
    char (*p8)[10]=a;    	//waring
}

对上面五个警告做下分析:

警告1:左值为元素个数为5的数组指针,但是右值是指向元素第一个元素首地址的指针,类型不符;

警告2:左值为元素个数为3的数组指针,但是右值是指向含5个元素的整个数组的首地址,类型不符;

警告3:左值为元素个数为3的数组指针,但是右值是指向含5个元素的数组第一个元素的首地址,类型不符;

警告3/4同2/3。

然后分析下打印出来的值:

由于左右值类型不兼容,从右值赋给左值之前先对右值进行了左值类型的强制转换。

然后就容易理解:

为什么p4+1的值是0xbffff2ba,而不是0xbffff2b6(未强制转换前); 强制转换后a指向整个数组的首地址,与&a相同;

为什么p5+1的值是0xbffff2b8,而不是0xbffff2ba;强制转换后&a由指向含5个元素的整个数组的首地址变为指向含3个元素的整个数组的首地址。

为什么p6+1的值是0xbffff2b8,而不是0xbffff2b6;这里是前面两种强制转换的综合结果。

后面雷同。

地址的强制类型转换

先看个实例

struct Test
{
    int Num;
    char *pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}*p;

假设p的值为0x1000000。

p+0x1=?

(unsigned long)p+0x1=?

(unsigned int*)p+0x1=?

实质上是看编译器怎么处理指针变量和一个整数相加减。类似前面a+1和&a+1的区别。

指针变量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。这个整数的单位不是byte而是元素的个数;

看强制类型转换博客中关于运算符的部分:http://www.cnblogs.com/kwseeker-bolgs/p/4488907.html

所以:

1. p+0x1=0x1000000+sizeof(Test)*0x1=0x1000014;

关于为什么这里的sizeof(Test)=20,后面详细介绍。

2. (unsigned long)p+0x1=?这里涉及强制类型转换,将struct类型转换为unsigned long,所以两者现在都是无符号长整型,直接相加:

(unsigned long)p+0x1=0x1000000+0x1=0x1000001;

3. (unsigned int*)p+0x1=?前者是无符号整形,后者也需要转换为无符号整形:

(unsigned int*)p+0x1=0x1000000+sizeof(unsigned int)*0x1=0x1000004;

4. (unsinged int)p+0x1=0x1000000+0x1=0x1000001;

这里要非常注意(unsinged int)与(unsinged int*)的区别,一个是按无符号整形数据处理,一个是按指针处理。

调试结果如下:这里的p=0x0;

一个例题:

int main()
{
    int a[4]={1,2,3,4};
    int *ptr1=(int *)(&a+1);    
    int *ptr2=(int *)((int)a+1);

    printf("%x, %x", ptr1[-1],*ptr2);  //输出4,2000000
    return 0;
}

分析:ptr1[-1] 解析为*(ptr1-1)

 

对于为什么会出现2000000,首先需要弄清系统大小端存储模式。

int checkSystem()
{
    union check
    {
        int i;
        char ch;
    }
    c.i=1;
    return(c.ch==1);
}
//返回0为大端模式,返回1为小端模式

经过验证系统采用小端模式。所以*ptr2=0x02000000。

二维数组与二级指针

二维数组

二维数组char a[3][4]中a[i][j]元素的首地址是:a+i*sizeof(char)*4+j*sizeof(char),指针形式*(*(a+i)+j)。

一个陷阱题:

#include <stdio.h>
int main()
{
    int a[3][2]={(0,1),(2,3),(4,5)};
    int *p;
    p=a[0];
    printf("%d",p[0]);
}

读题时很容易忽略大括号里面嵌套的不是大括号而是小括号(即逗号表达式)。

上面的其实是int a[3][2]={1,3,5}={{1,3},{5,0},{0,0}};

int a[5][5];

int (*p)[4];

p=a;

问&p[4][2]-&a[4][2]的值是多少?

关键问题是int (*p)[4] 到 &p[4][2]是怎样的一个过程?

根据定义:p是一个指向包含4个元素的数组的指针。也就是说p+1表示的是指针p向后移动一个"包含4个int类型元素的数组",这里1的单位是4*sizeof(int)。所以p[4]相对于p[0]来说是向后移动了4*(4*sizeof(int))。由于p被初始化为&a[0],那么& p[4][2]=&a[0][0]+4*4*sizeof(int)+2*sizeof(int)。(0x72-0x88bytes)

数组参数与指针参数

1. 不能向函数以数组的形式传递一个数组,实参只能以指针的形式传递数组。但是函数定义的时候形参可以是数组形式。

C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。

#include <stdio.h>

void func(char a[10]){
	char c=a[3];
}
void func1(char *p){
	char c=p[3]; //或者 char c = *(p+3);
}
void func2(char a[]){
	char c=a[3];
}

int main(int argc, char const *argv[])
{
	char b[10]="abcdefg";
	//func(b[10]);		//此处的b[10]并不代表一个数组,而是一个越界的数组元素
	func(b);         //后面三个都是可以成功传值的
	func1(b);
	func2(b);

	return 0;
}

  

2.一级指针参数

1)能否把指针变量本身传递给一个函数

#include <stdio.h>

void func(char *p){
	char c=p[3]; //或者 char c = *(p+3);
}

int main(int argc, char const *argv[])
{
	char *p2="abcdefg";
	func(p2);  //这样操作没有语法错误,但是此句所做的操作只是对拷贝做的,在main中没有意义

	return 0;
}

  

二维数组参数与二维指针参数

void fun(char a[3][4]); 还可写成 void fun(char a[][4]);

由上面的规则可以改写为

void fun(char (*p)[4]);  //p指向a[3]的首地址

函数指针与函数指针数组

下面转自http://patmusing.blog.163.com/blog/static/1358349602010182102157/

什么是函数指针?

函数指针指向的是特殊的数据类型,函数的类型是由其返回的数据类型和其参数列表共同决定的,而函数的名称则不是其类型的一部分

一个具体函数的名字,如果后面不跟调用符号(即括号),则该名字就是该函数的指针(注意:大部分情况下,可以这么认为,但这种说法并不很严格)。

函数指针的声明方法

// 定义函数指针pf

int (*pf)(const int&, const int&);                                                (1)

上面的pf就是一个函数指针,指向所有返回类型为int,并带有两个const int&参数的函数。注意*pf两边的括号是必须的,否则上面的定义就变成了:

int *pf(const int&, const int&);                                                   (2)

而这声明了一个函数pf,其返回类型为int *, 带有两个const int&参数。

typedef定义函数指针类型

// 定义函数指针类型cmpFun

typedef int (*cmpFun)(const int&, const int&);                      (3)

或许这样写更容易理解 typedef  int (*)(const int&, const int&)  cmpFun;

这样,cmpFun就成了一种数据类型,可以用它来声明和定义形如(1)式中的pf那样的函数指针,比如:

cmpFun pf = 0;

cmpFun pf = someFunction;

举个例子来说明一下:

#include <iostream>
#include <string>
using namespace std;

// 定义函数指针pf
int (*pf)(const int&, const int&);   // int (*)(const int&, const int&) pf;

// 定义函数指针类型cmpFun
typedef int (*cmpFun)(const int&, const int&);

// 具体函数
int intCompare(const int& aInt, const int& bInt)
{
         if(aInt == bInt) return 0;
         if(aInt > bInt)
         {
                   return 1;
         }
         else
         {
                   return -1;
         }
}

int main(void)
{
         int aInt = 1;
         int bInt = 2;
         pf = intCompare;
         // pf = &stringCompare;              // 和上面一句是完全一样的
         // 使用pf
         if(pf(aInt, bInt) == 0)
         {
                   cout << "two integers are equal" << "." << endl;
         }
         else if(pf(aInt, bInt) > 0)
         {
                   cout << aInt << " is greater than " << bInt << "." << endl;
         }
         else
         {
                  cout << aInt << " is less than " << bInt << "." << endl;
         }

         cout << "------------------------" << endl;
         // 用函数指针类型cmpFun声明并初始化一个函数指针pf2
         cmpFun pf2 = intCompare;
         // 使用pf2
         if(pf2(aInt, bInt) == 0)
         {
                   cout << "two integers are equal" << "." << endl;
         }
         else if(pf(aInt, bInt) > 0)
         {
                   cout << aInt << " is greater than " << bInt << "." << endl;
         }
         else
         {
                   cout << aInt << " is less than " << bInt << "." << endl;
         }

         return 0;
}

函数指针作为参数

函数指针可以作为一个函数的参数,如下两种办法可以做到这一点:

(a) int plusFun(int&, int&, int (const int&, const int&));

(b) int plusFun(int&, int(*)(const int&, const int&));

以上两个方式做到的是类似的事情:(a)中的plusFun函数的第三个参数就是一个函数指针, (b)中的第二个参数也是一个函数指针。下面我们分别定义前面声明的两个plusFun函数。

(a)中的plusFun定义如下:

//函数指针作为参数:错误的做法

//int plusFun(int& aInt, int& bInt, int paf(const int& cInt, const int& dInt))

//{

//

//       return aInt + bInt + paf(cInt, dInt);

//}

//函数指针作为参数:正确的做法

int plusFun(int& aInt, int& bInt, int paf(const int &, const int &))

{

int cInt = 2;

int dInt = 1;

return aInt + bInt + paf(cInt, dInt);

}

调用plusFun的代码:

pf = intCompare;

// 函数指针作为参数

int aaInt = 3;

int bbInt = 4;

cout << plusFun(aaInt, bbInt, pf) << endl;

(b)中的plusFun定义如下:

//函数指针作为参数:错误的做法

//int plusFun(int& aInt, int(*paf2)(const int& bInt, const int& cInt))

//{

//       return aInt + paf2(bInt, cInt);

//}

//函数指针作为参数:正确的做法

int plusFun(int& aInt, int(*paf2)(const int&, const int&))

{

int bInt = 1;

int cInt = 2;

return aInt + paf2(bInt, cInt);

}

调用plusFun的代码:

cmpFun pf2 = intCompare;

// 函数指针作为参数

int aaInt = 3;

cout << plusFun(aaInt, pf2) << endl;

函数指针作为返回值

一个函数的返回值可以是一个函数指针,这个声明形式写起来有点麻烦:

// 函数指针作为返回值

int (*retFunPointer(int))(const int&, const int&);

上面的声明的含义:

a)       retFunPointer是一个函数,该函数有一个int类型的参数;

b)       retFunPointer返回值是一个函数指针,它指向的是带有两个const int&类型参数,且返回类型为int的函数。

retFunPointer的定义:

// 函数指针为返回值

int (*retFunPointer(int aInt))(const int&, const int&)

{

cout << aInt << endl;

// pf已经在前面定义过了

return pf;

}

调用代码示例:

// 函数指针作为返回值,retFunPointer返回一个cmpFun类型的函数指针

cmpFun pf3 = retFunPointer(aaInt);

int result = pf3(aaInt, bbInt);

cout << result << endl;

包含上面所有情况的完整代码

#include <iostream>
#include <string>
using namespace std;

// 定义函数指针pf
int (*pf)(const int&, const int&);

// 定义函数指针类型cmpFun
typedef int (*cmpFun)(const int&, const int&);

// 函数指针作为参数
int plusFun(int&, int(const int&, const int&));
int plusFun(int&, int(*)(const int&, const int&));

// 函数指针作为返回值
int (*retFunPointer(int))(const int&, const int&);

// 具体函数
int intCompare(const int& aInt, const int& bInt)
{
         if(aInt == bInt) return 0;
         if(aInt > bInt)
         {
                   return 1;
         }
         else
         {
                   return -1;
         }
}

//函数指针作为参数:错误的做法
//int plusFun(int& aInt, int& bInt, int paf(const int& cInt, const int& dInt))
//{
//
//       return aInt + bInt + paf(cInt, dInt);
//}

//函数指针作为参数:正确的做法
int plusFun(int& aInt, int& bInt, int paf(const int &, const int &))
{
         int cInt = 2;
         int dInt = 1;
         return aInt + bInt + paf(cInt, dInt);
}

//函数指针作为参数:错误的做法
//int plusFun(int& aInt, int(*paf2)(const int& bInt, const int& cInt))
//{
//       return aInt + paf2(bInt, cInt);
//}

//函数指针作为参数:正确的做法
int plusFun(int& aInt, int(*paf2)(const int&, const int&))
{
         int bInt = 1;
         int cInt = 2;
         return aInt + paf2(bInt, cInt);
}

// 函数指针为返回值
int (*retFunPointer(int aInt))(const int&, const int&)
{
         cout << aInt << endl;
         // pf已经在前面定义过了
         return pf;
}

int main(void)
{
         int aInt = 1;
         int bInt = 2;
         pf = intCompare;
         // pf = &stringCompare;              // 和上面一句是完全一样的
         // 使用pf
         if(pf(aInt, bInt) == 0)
         {
                   cout << "two integers are equal" << "." << endl;
         }
         else if(pf(aInt, bInt) > 0)
         {
                   cout << aInt << " is greater than " << bInt << "." << endl;
         }
         else
         {
                   cout << aInt << " is less than " << bInt << "." << endl;
         }

         cout << "------------------------" << endl;
         // 用函数指针类型cmpFun声明并初始化一个函数指针pf2
         cmpFun pf2 = intCompare;
         // 使用pf2
         if(pf2(aInt, bInt) == 0)
         {
                   cout << "two integers are equal" << "." << endl;
         }
         else if(pf(aInt, bInt) > 0)
         {
                   cout << aInt << " is greater than " << bInt << "." << endl;
         }
         else
         {
                   cout << aInt << " is less than " << bInt << "." << endl;
         }
         cout << "------------------------" << endl;

         // 函数指针作为参数
         int aaInt = 3;
         int bbInt = 4;
         cout << plusFun(aaInt, bbInt, pf) << endl;
         cout << plusFun(aaInt, pf2) << endl;
         cout << "------------------------" << endl;

         // 函数指针作为返回值,retFunPointer返回一个cmpFun类型的函数指针
         cmpFun pf3 = retFunPointer(aaInt);
         int result = pf3(aaInt, bbInt);
         cout << result << endl;

         return 0;
}

  

时间: 2024-10-26 20:38:52

C语言指针与数组的定义与声明易错分析的相关文章

指针和数组的定义与声明

情况一:定义为数组,声明为指针 文件1中定义如下: char array[100]; 文件2声明如下: extern char *array; 分析:假如array[100]中存的是"abcde.........."; extern char *array;编译器认为array是一个指针变量为其分配4个字节. 然而,array[100]中的存储方式为: 0X41 0X42 0X43 0X44 则*array取四个字节为0X41424344(没有考虑大端小端),这个地址为谁的地址,鬼知道

c/c++(疑3) C语言指针与数组之间关系

c/c++ (疑1)数组和指针 c/c++(疑2) const extern 有了前面 两篇 基础,下面我们可以更深入的来介绍c/c++(疑3) C语言指针与数组之间关系 1 概述(C语言指针与数组之间关系) 指针就是指针,指针变量在32 位系统下,永远占4 个byte,其值为某一个内存的地址.指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到. 数组就是数组,其大小与元素的类型和个数有关.定义数组时必须指定其元素的类型和个数.数组可以存任何类型的数据,但不能存函数. 2    

C语言指针和数组

C语言指针和数组 binsearch else-if  shellsort  insertsort 指针和地址 指针是编程语言中的一类数据类型及其对象或变量,用来表示或存储一个内存地址,这个地址的值直接指向(points to)存在该地址的对象的值. 取值运算*p返回保存在内存地址为p的内存空间中的值.取地址&p运算则返回操作数p的内存地址 C语言是以传值的方式将参数值传递给被调用函数,被调用函数不能直接修改主调函数中的值,需要修改主调函数中的值就需要利用指针 指针和数组 数组名代表的是该数组的

程序设计基石与实践之C语言指针和数组基础

英文出处:Dennis Kubes:  <Basics of Pointers and Arrays in C>. 关于C语言中指针和数组的争论就像是一场恶战.一方面,有些人觉得,所有人都必须承认指针与数组是不同的.而另一些人则认为数组被当成指针来处理,因此它们不应该有什么区别.这种现象让人迷惑.然而,这两种说法其实都是正确的. 数组不是指针,指针也不能说是数组.在C语言中,指针仅在内存中代表一个地址,而数组是许多连续的内存块,多个类型相似的元素存储在其中.更深入的解释,请参考我之前的博文&l

C语言指针与数组易混淆知识点(一)

一指针与数组 二指针与函数 三指针数组数组指针指向指针的指针 四程序陷阱 一.指针与数组 指针:指针本身也是一个变量,它的内容是指向的内容的地址.指针同样有类型的区分,char 的指针只能指向char型数据,int 指针指向int型数据.但是指针所占内存单元的大小(即其内容)是跟操作系统的地址位数有关,比如32位地址的系统,那么指针所占的内存单元就是4个字节,16位就是2个字节,因此,指针的类型只是限定它所指向的变量的类型,其内容所占的单元大小是与操作系统的地址位数相关,与变量类型无关. 在32

C语言指针、数组和函数的一些总结

主要来自于孟宪福老师的分布式对象课程. int a[7][8]; // a指向 a-->a[0]-->a[0][0] a[0]是第0行一维数组 // a + i --> a[i] --> a[i][0] a[i][j]; // 等价于*( *(p + i ) + j) 定义一个函数返回7*8的数组a int ( *f () ) [8] { int a[7][8]; return a; } 总结起来就是: 1. 从标识符开始 a. 定义数组,数组的元素是什么? b. 定义函数,函数的

指针与数组的关联3 --声明

当我们在调用函数时,传递数组,就是把数组首元素的地址传递给函数,本质也就是给函数传递了一个指针变量.这时候我们会想那么数组是不是跟指针是一样的.为了验证,我们可以给一个项目里创建一个新的源文件,里面创建一个指针变量和是个数组,然后在另一个文件中用数组的方法声明指针,在用指针的方法声明这个数组进行调试,看下结果会如何. 我们创建一个test.c文件用来编写主函数,test1.c创建变量编写内容如下: test.c: #include <stdio.h> extern char*arr; exte

Java数组的定义,声明,初始化和遍历

数组的定义 数组是相同类型数据的有序集合.数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成.其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们.数组的三个基本特点: 1. 长度是确定的.数组一旦被创建,它的大小就是不可以改变的. 2. 其元素必须是相同类型,不允许出现混合类型. 3. 数组类型可以是任何数据类型,包括基本类型和引用类型. 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量.数组本身就是对象,Java中对象是在堆中

C语言中的指针与数组的定义与使用

指针的特点 他就是内存中的一个地址 指针本身运算 指针所指向的内容是可以操作的 操作系统是如何管理内存的 栈空间 4M~8m的大小 当进入函数的时候会进行压栈数据 堆空间 4g的大小 1g是操作系统 全局变量 内存映射 可以对内存的内容修改修改硬盘的内容 一般在数据库中经常使用 内存的分配与释放 c语言分配内存的方法 // malloc(需要分配的大小): 这里的分配的大小需要对齐的2的指数 void *mem = malloc(size); 释放内存 // 一般分配的内容都是在堆空间中的 //