{//=======指针和数组
{//指针数组的定义
{//用变量a给出下面的定义
a) 一个整型数(An integer)
b) 一个指向整型数的指针(A pointer to an integer)
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)
d) 一个有10个整型数的数组(An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
}
{//引用与指针有什么区别?
1) 引用必须被初始化,指针不必。
2) 引用初始化以后不能被改变,指针可以改变所指的对象。
3) 不存在指向空值的引用,但是存在指向空值的指针。
引用的作用范围:
通常是作为函数传参使用,可以尽量减少使用指针,还可以提升效率。
引用的作用:对变量取别名
引用变量必须初始化,表名是给谁取别名
int a = 5;
int &b = a;//给a变量取别名b,b的类型:int &
}
{//char * const p; char onst * p const char *p 三个有什么区别?
char * const p; //常量指针,p的值不可以修改
char const * p; //指向常量的指针,指向的常量值不可以改
const char *p; //和char const *p
}
{//要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234; 那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
答案:((void ()( ))0x100000 ) ( );
首先要将0x100000强制转换成函数指针,即:
(void ()())0x100000
//(unsigned int)0x100000
// void ()() unsigned int
// int (a)(int);
// void ()();
// do();
然后再调用它:
((void ()())0x100000)();
用typedef可以看得更直观些:
typedef void()() voidFuncPtr;
*((voidFuncPtr)0x100000)();
}
}
{//指针数组的大小
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
打印结果是什么?
{//解答:
结果是:0 0 1 1
str1,str2,str3,str4是数组变量,它们有各自的内存空间;
而str5,str6,str7,str8是指针,它们指向相同的常量区域
}
-----------------------------------------------
以下代码中的两个sizeof用法有问题吗?
void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母
{
for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )
if( 'a'<=str[i] && str[i]<='z' )
str[i] -= ('a'-'A' );
}
char str[] = "aBcDe";
cout << "str字符长度为: " << sizeof(str)/sizeof(str[0]) << endl;
UpperCase( str );
cout << str << endl;
{//解答:
函数内的sizeof有问题。
根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。
函数外的str是一个静态定义的数组,因此其大小为6,
函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,
因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。
}
-----------------------------------------------
int main()
{
char a[10];
char b[] = "hello";
printf("%d\n", sizeof(a));
printf("%d\n", strlen(a));
printf("%d\n", sizeof(b));
printf("%d\n", strlen(b));
}
{//解答:
10 10 6 5
//strlen 遇\0 结束, \0不计入长度 所以在为char型的时候还是不一样的
}
}
{//从存储角度看,指针数组
. main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
输出结果是什么?
{//解答
输出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int *ptr=(int *)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
a是长度为5的int数组指针,所以要加 5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,
a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].
}
--------------------------------------------------------
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *p1 = (int*)(&a + 1);
int *p2 = (int *)((int)a + 1);
printf("0x%x, 0x%x\n", p1[-1], *p2);
return 0;
}
变量a的内存地址为:0x0034fc50 问printf输出值是多少
{//解答
首先分析p1
&a为数组指针,类型是"int (*)[5]",数组指针是指向数组首元素的地址的指针
&a的内存地址为:0x0034fc50
&a + 1指针加一,向后移动一个元素,这里的元素类型是"int (*)[5]",其空间大小为20字节,因此
&a + 1的内存地址为:0x0034fc64(0x0034fc50 + 0x14)
p1的内存地址为:0x0034fc64
p1[-1]数组下标为负数,向前移动一个元素(注:数组越界在编译时与运行时都不会报错,除非操作非法内存)
p1[-1]元素所在的地址为:0x0034fc60
因此,可知p1[-1]的地址为a + 4的地址
p1[-1] == *(a + 4) == 5
继续分析p2
(int)a的值为0x0034fc50
(int)a + 1等于0x0034fc51
分析内存结构,测试环境字节序为little endian
01 00 00 00 02 00 00 00 03 00 0...
*p2等于0x2000000
}
}
{ //函数调用中传递指针
#include <stdio.h>
#include <stdlib.h>
void getmemory(char *p)//改为void getmemory(char **p)
{
p=(char *) malloc(100);
strcpy(p,“hello world”);
}
int main( )
{
char *str=NULL;
getmemory(str);
printf(“%s/n”,str);
free(str);
return 0;
}
{//解答
程序崩溃,getmemory中的malloc 不能返回动态内存, free()对str操作很危险
getmemory中p是形参,是一个指针变量,getmemory(str)调用后,传入的是指针变量保存的对象地址,
p=(char *) malloc(100)实际上是把申请的动态内存空间的首地址付给p指向的地址(即str指向的地址null),
这个是错误的。
应该修改成指向指针的指针 void getmemory(char **p),
这样malloc返回的地址付给*p(即str变量本身)。
}
-----------------------------------------
void GetMemory(char **p,int num)
{ //p,指向指针的指针,*p,p指向的指针(即str),**p,最终的对象,str指向的单元
*p=(char *)malloc(num); //申请空间首地址付给传入的被p指向的指针,即str
}
int main()
{
char *str=NULL;
GetMemory(&str,100); //传入指针变量本身的地址
strcpy(str,"hello");
free(str);
//str=NULL;
if(str!=NULL)
{
strcpy(str,"world");
}
printf("\n str is %s",str);
}
问输出结果是什么?
{//解答
答案:输出str is world。
free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.
此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,
尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。
这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。
当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),
并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。
}
}