1) int (*p)();
int (*p[])();
int (*p)[6];
C语言的声明的优先级规则如下: ---------------------------------------------------------------
A 声明从它的名字开始读取,然后按照优先级顺序依次读取
B 优先级从高到低依次是:
B.1 声明中被括号括起来的那一部分
B.2 后缀操作符【圆括号()表示这是一个函数,方括号[]表 示这是一个数组】
B.3 前缀操作符【*表示这是一个指向……的指针】
C 如果const或volatile关键字的后面紧跟说明符【如int、long等】, 那么它作用于类型说明符。
在其他情况下,const和【或】volatile 关键字作用于它左边紧邻的指针星号。
----------------------------------------------------------------
下面看一个例子: char * const *(*next)();
-------- ------------------------------------------------------
运用规则 解释 -------- ------------------------------------------------------
A 首先,看变量名“next”,并注意到它直接被括号括住
B.1 先把括号里面的东西看成一个整体,得出“next是一个指向…… 的指针”
B 然后考虑括号外面的东西,在星号前缀和括号后缀之间做出选择
B.2 B.2规则告诉我们优先级较高的是右边的函数括号,所以得出“next 是一个函数指针,指向一个返回……的函数”
B.3 然后,处理前缀“*”,得出指针所指的内容
C 最后,把“char* const”解释为指向字符的常量指针
-------- ------------------------------------------------------
经过上述分析,得出“next是一个指针,它指向一个函数,该函数返回另外一个 指针,
该指针【函数返回的】指向一个类型为char的常量指针”。
千万不要嫌麻烦,习惯了之后就会很自然地看出各种声明了,
再例如: float (*op[4])(float a,float b); 用上面的步骤,可以得出“op是个含有四个元素的数组,
它的元素的类型是指针, 该指针是函数指针,该函数接受两个float参数,并返回float。
2) int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d\n",*(a+1),*(ptr-1));
answer: 2,5
3) int main()
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return 0;
}
answer:Run_Time Check Failure #2 - Stack around the variable 'a' was corrupted.
没有为str分配内存空间,将会发生异常问题出在将一个字符串复制进一个字符变量指针所指地址。
虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
int main()
{
char a[10]={0};
char *str=a;
strcpy(str,"hello");
printf(str);
system("pause");
return 0;
}
answer:
hello
4)
#include "stdafx.h"
#include "stdlib.h"
#include <string.h>
#include <iostream>
using namespace std;
int main()
{
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;
system("pause");
return 0;
}
结果是:0 0 1 1 str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。
5)
#include "stdafx.h"
#include "stdlib.h"
#include <string.h>
#include <iostream>
using namespace std;
int main()
{
char* s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
system("pause");
return 0;
}
答案:"AAA"是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。
cosnt char* s="AAA"; 然后又因为是常量,所以对是s[0]的赋值操作是不合法的。
6)交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3;
答案:有两种解法, 一种用算术算法, 一种用^(异或)
a = a + b; b = a - b; a = a - b;
或 a = a ^ b; b = a ^ b; a = a ^ b;
7)
#include "stdafx.h"
#include "stdlib.h"
#include <string.h>
#include <iostream>
using namespace std;
void getmemory(char *p)
{
p=(char *) malloc(100);
strcpy(p,"hello world");
}
int main( )
{
char *str=0;
getmemory(str);
printf("%s\n",str);
free(str);
system("pause");
return 0;
}
答案:程序崩溃,getmemory中的malloc 不能返回动态内存, free()对str操作很危险
注:VC8库文件程序不崩溃,屏幕打印出 <null>
8)
int main( )
{
char szstr[10];
strcpy(szstr,"0123456789");
system("pause");
return 0;
}
答案:长度不一样 strcpy()函数会复制整个src的内容并添加终止 NULL字符,即使src比dest长。
9)
用C语言实现汇编语言的跳转指令:
要对绝对地址0x100000赋值,我们可以用 (unsigned int*)0x100000 = 1234;
那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
答案:(*((void (*)( ))0x100000 )) ( );
10)
int main( )
{
unsigned short A = 10;
printf("~A = %d\n", ~A);
char c=128;
printf("c=%d\n",c);
system("pause");
return 0;
}
答案:
~A = -11
c=-128
11)用两个栈实现一个队列的功能?要求给出算法和思路!
答案:设2个栈为A,B, 一开始均为空.
入队: 将新元素push入栈A;
出队: (1)判断栈B是否为空;
(2)如果为空,则将栈A中所有元素依次pop出并push到栈B; (3)将栈B的栈顶元素pop出;
12)
int main()
{
int a,b,c,d;
a=10;
b=a++;
c=++a;
d=10*a++;
printf("b,c,d:%d,%d,%d",b,c,d);
system("pause");
return 0;
}
answer: b,c,d:10,12,120请按任意键继续. . .
13)char str[ ]= "Hello";
char *p=str; int n=10;
sizeof(str)=? sizeof(p)=? sizeof(n)=?
void func(char str[100]){ } sizeof(str)=?
答案:6,4,4,4,
14)
int main()
{
int arr[]={6,7,8,9,10};
int *ptr=arr;
*(ptr++)+=123;
printf("%d,%d",*ptr,*(++ptr));
system("pause");
return 0;
}
答案:VC中先*(++ptr) 后*ptr,于是结果为8,8
15)
int main()
{
char string[10], str1[10];
for(int I=0; I <10;I++)
{
str1[I] ='a';
}
strcpy(string, str1);
system("pause");
return 0;
}
答案:strcpy使用错误,strcpy只有遇到字符串末尾的'\0'才会结束,而str1并没有结尾标志,导致strcpy函数越界访问,不妨让str1[9]='\0',这样就正常了。
16)
void test3(char* str1)
{
char string[10];
if(strlen(str1) <=10)
{
strcpy(string, str1);
}
}
答案:strlen(str1)算出来的值是不包含结尾符'\0'的,如果str1刚好为10个字符+1结尾符,string就得不到结尾符了。可将strlen(str1) <=10改为strlen(str1) <10
17)
int a[3];
a[0]=0;
a[1]=1;
a[2]=2;
int *p, *q;
p=a; q=&a[2];
则a[q-p]=?
答:a[q-p]=a[2]=2;这题是要告诉我们指针的运算特点
18)函数原形已经给出:int p(int i, int N);
功能:调用该函数,打印如下格式的输出,例p(1, 7);
1
2
3
4
5
6
7
6
5
4
3
2
1
即每行一个数字。(注意:N只打印一次)
p(2, 8);
2
3
4
5
6
7
8
7
6
5
4
3
2
要求:
1. 函数中唯一能够调用的函数就是printf。
2. 不准使用如下的关键字:typedef, enum, do, while, for, switch, case, break, continue, goto, if。
3. 不能使用逗号表达式和?:表达式。
4. 函数中只能有一条语句。
答案:
int p(int i, int n)
{
return printf("%d\n",i)&&(i==n||(p(i+1,n)&&printf("%d\n",i)));
}
写整齐一点是:
int p(int i, int n)
{
return printf("%d\n",i)
&&
(
i==n
||
(p(i+1,n)
&&printf("%d\n",i))
);
}
19)
int main()
{
unsigned int x=1;
signed char y=-1;
if(x>y)
printf("x>y");
else
printf("x<=y");
system("pause");
return 0;
}
答案:x<=y
int main()
{
unsigned int x=1;
signed int y=-1;
if(x>y)
printf("x>y");
else
printf("x<=y");
system("pause");
return 0;
}
答案:x<=y
int main()
{
unsigned char x=1;
signed char y=-1;
if(x>y)
printf("x>y");
else
printf("x<=y");
system("pause");
return 0;
}
答案:x>y
20)
int main()
{
int i=0;
i = '1234';
printf("0x%x\n",i);
system("pause");
return 0;
}
答案:0x31323334
多字符常量:
这种用法是不可移植的,因为有些实现可能不允许,
而且不同实现的整数长度和字节顺序可能不同。
int main()
{
int i=0;
i = '12345';
printf("0x%x\n",i);
system("pause");
return 0;
}
答案:常量中的字符太多,编译不通过
int main()
{
int i=0;
i = '\0x01';
printf("0x%x\n",i);
system("pause");
return 0;
}
答案:0x783031
\0是一个转义字符x的ascII码为0x78
以上是多字符常量的用法,由实现定义,这种用法是不可移植的,而且不同实现的整数长度和字节顺序可能不同。
二、C++中const用法总结
1. const修饰普通变量和指针
const修饰变量,一般有两种写法:
const TYPE value;
TYPE const value;
这两种写法在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的。
对于一个非指针的类型TYPE,无论怎么写,都是一个含义,即value只不可变。
例如:
const int nValue; //nValue是const
int const nValue; // nValue是const
但是对于指针类型的TYPE,不同的写法会有不同情况,例如:
A. const char *pContent;
B. char * const pContent;
C. char const *pContent;
D. const char* const pContent;
这样就一目了然。根据对于const修饰非指针变量的规则,很明显,A=C.
- 对于A,C, const修饰的类型为char的变量*pContent为常量,因此,pContent的内容为常量不可变.
- 对于B, 其实还有一种写法: const (char*) pContent;
含义为:const修饰的类型为char*的变量pContent为常量,因此,pContent指针本身为常量不可变.
- 对于D, 其实是A和B的混合体,表示指针本身和指针内容两者皆为常量不可变
还有其中区别方法:
沿着*号划一条线,
如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。
2. const修饰函数参数
const修饰函数参数是它最广泛的一种用途,它表示函数体中不能修改参数的值(包括参数本身的值或者参数其中包含的值)。它可以很好
void function(const int Var); //传递过来的参数在函数内不可以改变
void function(const char* Var); //参数指针所指内容为常量不可变
void function(char* const Var); //参数指针本身为常量不可变
参数为引用,为了增加效率同时防止修改。
修饰引用参数时:
void function(const Class& Var);//引用参数在函数内不可以改变
void function(const TYPE& Var); //引用参数在函数内为常量不可变
3. const 修饰函数返回值
const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。
(1) const int fun1() 这个其实无意义,因为参数返回本身就是赋值。
(2) const int * fun2()
调用时 const int *pValue = fun2();
我们可以把fun2()看作成一个变量,那么就是我们上面所说的1.(1)的写法,即指针内容不可变。
(3) int* const fun3()
调用时 int * const pValue = fun2();
我们可以把fun2()看作成一个变量,那么就是我们上面所说的1.(2)的写法,即指针本身不可变。
4. const修饰类对象/对象指针/对象引用
const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。
const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
例如:
class AAA
{
void func1();
void func2() const;
}
const AAA aObj;
aObj.func1(); ×
aObj.func2(); 正确
const AAA* aObj = new AAA();
aObj->func1(); ×
aObj->func2(); 正确
5. const修饰成员变量
const修饰类的成员函数,表示成员常量,不能被修改,同时它只能在初始化列表中赋值。
class A
{
…
const int nValue; //成员常量不能被修改
…
A(int x): nValue(x) {}; //只能在初始化列表中赋值
}
6. const修饰成员函数
const修饰类的成员函数,则该成员函数不能修改类中任何非const成员函数。一般写在函数的最后来修饰。
class A
{
…
void function()const; //常成员函数, 它不改变对象的成员变量. 也不能调用类中任何非const成员函数。
}
对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。
7. const常量与define宏定义的区别
(1) 编译器处理方式不同
define宏是在预处理阶段展开。
const常量是编译运行阶段使用。
(2) 类型和安全检查不同
define宏没有类型,不做任何类型检查,仅仅是展开。
const常量有具体的类型,在编译阶段会执行类型检查。
(3) 存储方式不同
define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
const常量会在内存中分配(可以是堆中也可以是栈中)。
三、C++成员函数指针
C++中,成员指针是最为复杂的语法结构。但在事件驱动和多线程应用中被广泛用于调用回叫函数。
在多线程应用中,每个线程都通过指向成员函数的指针来调用该函数。在这样的应用中,如果不用成员指针,
编程是非常困难的。C++成员函数指针在Wince/Windows Mobile的LCD driver代码中有精彩的应用,还有关于
COM实现的应用等。
1.成员函数指针是全局变量或局部变量:
#include "stdafx.h"
#include "stdlib.h"
#include <string.h>
#include <iostream>
using namespace std;
class CDirection{
public:
void up()
{
cout<<"up"<<endl;
}
void down()
{
cout<<"down"<<endl;
}
};
int main()
{
//声明变量
void (CDirection::*p)()= &CDirection::up;
CDirection objDirection;
CDirection *pDirection = &objDirection;
//调用1
(objDirection.*p)(); //up
p=&CDirection::down;
//调用2
(pDirection->*p)(); //down
(&objDirection->*p)();//down
system("pause");
return 0;
}
2.成员函数指针是类的成员变量:
#include "stdafx.h"
#include "stdlib.h"
#include <string.h>
#include <iostream>
using namespace std;
class CDirection{
public:
void up()
{
cout<<"up"<<endl;
}
void down()
{
cout<<"down"<<endl;
}
void (CDirection::*p)();
CDirection()
{
p = &CDirection::up;
}
};
int main()
{
//声明变量
CDirection objDirection;
CDirection *pDirection = &objDirection;
//调用方法
(objDirection.*objDirection.p)(); //up
system("pause");
return 0;
}
3.成员函数指针做为函数形参:
#include "stdafx.h"
#include "stdlib.h"
#include <string.h>
#include <iostream>
using namespace std;
class CDirection{
public:
void up()
{
cout<<"up"<<endl;
}
void down()
{
cout<<"down"<<endl;
}
};
void f(void (CDirection::*p)(),CDirection &objDirection)
{
//调用方法
(objDirection.*p)();
}
int main()
{
CDirection objDirection;
f(&CDirection::up,objDirection);
system("pause");
return 0;
}
四、C++对象模型
五、变参函数
1.Windows and Wince/Windows Mobile
#ifdef MydebugMsg
void printfLjp(const char * printfexp,...)
{
va_list arglist;
va_start(arglist, printfexp);
FILE *fDebug=fopen ("\\debugMsg.txt", "a");
if(!fDebug) MessageBox(NULL,L"Please close debugCAMApp.txt file fisrt!",L"ljp",0);
vfprintf(fDebug,printfexp, arglist);
va_end(arglist);
fclose(fDebug);
}
void printfWLjp(const wchar_t * printfexp,...)
{
va_list arglist;
va_start(arglist, printfexp);
FILE *fDebug=fopen ("\\debugMsg.txt", "a");
if(!fDebug) MessageBox(NULL,L"Please close debugCAMApp.txt file fisrt!",L"ljp",0);
vfwprintf(fDebug,printfexp, arglist);
va_end(arglist);
fclose(fDebug);
}
#define printfljp printfLjp
#define printfwljp printfWLjp
#else
#define printfljp
#define printfwljp
#endif
2.MTK
#define pfwrite(x1,x2,x3,x4,x5) FS_Write(x4,x1,x3,(U32 *)x5)
#define pfclose(x) { FS_Commit(x); FS_Close(x); }
void printLog(const char * printfexp,...)
{
FILE_HANDLE fDebug;
U32 write_len = 5000;
static unsigned char logbuff[5000] = {0};
// va_list arglist;
// va_start(arglist, printfexp);
char * VarPara = &printfexp + 1;
vsprintf(strlen((const char *)logbuff)>5000 ? (char *)logbuff : (char *)(logbuff+strlen((const char *)logbuff)),
printfexp,VarPara );
//sprintf(strlen((const char *)logbuff)>5000 ? (char *)logbuff : (char *)(logbuff+strlen((const char *)logbuff)),
// "x=%d,y=%d,pStart->top=%d,pStart->bottom=%d,pStart->left=%d,pStart->right=%d\n",
// x,y,pStart->top, pStart->bottom, pStart->left, pStart->right);
fDebug = pfopen(L"c:\\MyDebug.txt", PFS_WRITE);
//vfprintf(fDebug,printfexp, arglist);
//va_end(arglist);
if(fDebug > 0)
{
pfwrite(logbuff, 1, sizeof(logbuff), fDebug, &write_len);
pfclose(fDebug);
}
else
{
return;
}
}
3.可变参数宏定义(c99新增经测试,VC6.0不可以,VS2005可以)
例如:
#define MyPrintf(...) fprintf(stderr,__VA_ARGS__)
MyPrintf("FILE: %s\nLINE:%d\nDATE:%s\nTIME:%s\n",__FILE__ , __LINE__ , __DATE__ , __TIME__);
4.变参函数例子2
typedef char * va_list;
define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
注:_INTSIZEOF(n)表示按sizeof(int)对齐,即_INTSIZEOF(n)是sizeof(int)的倍数,例sizeof(int)=4; sizeof(n)=7;则 _INTSIZEOF(n)=8
如:
Function(int argc, ...)
{
//arc 代表参数个数,假定参数全是整型,参数最大个数为10,调用方法如下:
int params[10];
va_list marker;
va_start( marker, argc );
for(i = 0; i < argc; i++)
{
params[i] = va_arg( marker, int);
//取得当前参数,marker 指向下一个参数地址
}
va_end( marker );
}