1.整数,指针,实数,BOOL变量和零值比较
整型:
int num;
if(num == 0);
if(num != 0)
指针变量:
char *ptr;
if(ptr == NULL)
if(ptr != NULL)
BOOL 变量:
BOOL flag;
if(flag);
if(!flag)
实数:
#define EPSIN 0.000001
float num2;
if((num2>=-EPSION)&&(num2<=EPSION))
int num3;
if(0 == num3) //编译正常,为了防止漏写一个=
if(0 = num3) //编译器报错,变量不能给常数赋值
if(num3 == 0) //正常,判断
if(num3 = 0) //编译不报错,但是这是赋值为0;
2. sizeof用法,strlen用法
一、sizeof的概念
sizeof是C语言的一种单目操作符,如C语言的其他操作符++、--等。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。
二、sizeof的使用方法
1、用于数据类型
sizeof使用形式:sizeof(type)
数据类型必须用括号括住。如sizeof(int)。
2、用于变量
sizeof使用形式:sizeof(var_name)或sizeof var_name
变量名可以不用括号括住。如sizeof (var_name),sizeof var_name等都是正确形式。带括号的用法更普遍,大多数程序员采用这种形式。
注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。
如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时char_v定义为char char_v [MAX]且MAX未知,sizeof(void)都不是正确形式。
三、sizeof的结果
sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。
1、若操作数具有类型char、unsigned char或signed char,其结果等于1。
ANSI C正式规定字符类型为1字节。
2、int(4)、unsigned int(4) 、short int(2)、unsigned short(2) 、long int(4) 、unsigned long (4)、float(4)、double(8)、long double(8)类型的sizeof 在ANSI C中没有具体规定,大小依赖于实现括号内为sizeof的结果。
3、当操作数是指针时,sizeof依赖于编译器。例如Microsoft C/C++7.0中,near类指针字节数为2,far、huge类指针字节数为4。一般Unix的指针字节数为4, 一般来说指针字节数为4。
例1:
int *p;
int a[5];
p = a;
sizeof(p)的结果是4。
例2:
const char* pstars[]={"aa","bb","cc","dd"};
const int starCount=size of pstars/sizeof pstars[0];
starCount是指针数组pstars中的元素个数4。
因为pstars是一个常指针数组,const代表指针中的数据不能修改,即不能使用诸如*pstars[0] = 'c';之类,会有编译错误的。
先放下const不谈,pstars的数组成员是4个指针,所以其占用的空间是4个指针所占用的空间即4*4 = 16,而pstars[0]是该数组的一个元素,即一个指针,所占空间即为4个字节。所以最后计算结果为4。
4、当操作数具有数组类型时,其结果是数组的总字节数。
例如:int a[12];sizeof(a)的结果是12 * 4 = 48。
5、联合类型操作数的sizeof是其最大字节成员的字节数。结构类型操作数的sizeof是这种类型对象的总字节数,包括任何垫补在内。
让我们看如下结构:
struct {char b; double x;} a;
vc6.0结果为16。
这是因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。如double类型的结构成员x要放在被8整除的地址。可以通过#pragma pack(n)改变字节对齐。
例如:
struct mystruct1{
char a; //1
int b; //4
short c; //2
long d; //4
double e; //8
};
默认的结构体的sizeof结果是24。
#pragma pack(2)
struct mystruct1{
char a; //1
int b; //4
short c; //2
long d; //4
double e; //8
};
此时结果为20。
我的推论:
默认时以最大字节作为存储单位,即8字节,而对齐方式则以两两之间的最大字节对齐,比如char与int以4字节对齐,占用了一个8字节空间,short重新开始一个8字节空间,short与long以4字节对齐,再结合成一个8字节空间,同时double单独占用一个8字节空间,所以最后总空间为24字节。
改变对齐方式为2字节时,则char以2字节后接int的2个2字节,再接short的2字节,再接long的2个2字节最后接double的4个2字节,此时计算总数为2字节 * (1+2+1+2+4)= 20字节。
注意:#pragma pack()可以还原为默认对齐方式,而要改变其对齐方式,#pragma pack(n)必须放在使用之前。也可以使用
#pragma pack(push) //保存对齐状态
#pragma pack(pop) //恢复对齐状态
这一对来进行对齐方式的改变。
C++中类也是类似结构体的sizeof的计算方式,计算类对象中数据成员所占用的空间,不过static对象不计算在内。
例如:
class A{
char b;
static double x;
public:
void count(int *aa);
};
此时sizeof(A)的结果是1。
6、如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。
例如:
void a(int *c)
{
printf("%d/n",sizeof(c));
}
void a(int c[13])
{
printf("%d/n",sizeof(c));
}
打印的都是4。
四、sizeof与其他操作符的关系
sizeof的优先级为2级,比/、%等3级运算符优先级高。它可以与其他操作符一起组成表达式。如i*sizeof(int);其中i为int类型变量。
五、sizeof的主要用途
1、sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如:
void *malloc(size_t size),
size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。
2、sizeof的另一个的主要用途是计算数组中元素的个数。例如:
void * memset(void * s,int c,sizeof(s))。
六、建议
由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。
解析C/C++语言中的strlen与sizeof的区别
1.从功能定义上,strlen函数,用来求字符串的长度,sizeof函数是用来求指定变量或变量类型等所占用内存的大小;
2.sizeof是运算符,而strlen是C库函数strlen只能用char*做参数,且以'/0'结尾的;
对于静态数组处理:
char str[20]="0123456789";
strlen(str)=10; //表示数组中字符串的长度
sizeof(str)=20; //表示数组变量分配的长度
对于指针处理:
char *str="0123456789";
strlen(str)=10; //表示字符串的长度
sizeof(str)=4; //表示指针变量的所占内存大小
sizeof(*str)=1; //表示'0'这个字符变量的所占内存大小
另附:
strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个'\0',如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,直到遇到'\0'停止。
char aa[10];cout<<strlen(aa)<<endl; //结果是不定的
char aa[10]={'\0'}; cout<<strlen(aa)<<endl; //结果为0
char aa[10]="jun"; cout<<strlen(aa)<<endl; //结果为3
而sizeof()返回的是变量声明后所占的内存数,不是实际长度,此外sizeof不是函数,仅仅是一个操作符,strlen是函数。
sizeof(aa) 返回10
int a[10]; sizeof(a) 返回40 (根据语言int型 c 是两个字节 c++是四个 java 是两个)
⒈sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。
该类型保证能容纳实现所建立的最大对象的字节大小。
⒉sizeof是操作符(关键字),strlen是函数。
⒊sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以''\0''结尾的。
sizeof还可以用函数做参数,比如:
short f();
printf("%d\n",sizeof(f()));
输出的结果是sizeof(short),即2。
⒋数组做sizeof的参数不退化,传递给strlen就退化为指针了。
⒌大部分编译程序 在编译的时候就把sizeof计算过了是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因
char str[20]="0123456789";
int a=strlen(str); //a=10;
int b=sizeof(str); //而b=20;
6.strlen的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小。
7.sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。
⒏当适用了于一个结构类型时或变量, sizeof 返回实际的大小,
当适用一静态地空间数组, sizeof 归还全部数组的尺寸。
sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸
⒐数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,
如:
fun(char [8])
fun(char [])
都等价于 fun(char *)
在C++里参数传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小
一、请填写BOOL , float, 指针变量与“零值”比较的 if 语句。(10 分)
提示:这里“零值”可以是0, 0.0 , FALSE 或者“空指针”。例如 int 变量 n 与“零值”
比较的 if 语句为:
if ( n == 0 )
if ( n != 0 )
以此类推。
请写出 BOOL flag 与“零值”比较的 if 语句:
if ( flag )
if ( !flag )
如下写法均属不良风格,不得分。
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)
请写出 float x 与“零值”比较的 if 语句:
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
不可将浮点变量用“==”或“!=”与数字
比较,应该设法转化成“>=”或“<=”此
类形式。
如下是错误的写法,不得分。
if (x == 0.0)
if (x != 0.0)
请写出 char *p 与“零值”比较的 if 语句:
if (p == NULL)
if (p != NULL)
如下写法均属不良风格,不得分。
if (p == 0)
if (p != 0)
if (p)
if (!)
二、以下为Windows NT 下的32 位C++程序,请计算sizeof 的值(10 分)
void Func ( char str[100])
{
请计算
}
sizeof( str ) =4
char str[] = “Hello” ;
char *p = str ;
int n = 10;
请计算
sizeof (str ) =6
sizeof ( p ) =4
sizeof ( n ) =4
void *p = malloc( 100 );
请计算
sizeof ( p ) =4
三、简答题(25 分)
1、头文件中的 ifndef/define/endif 干什么用?
作用:一个大的软件工程里面,可能多个文件同时包含同一个一个头文件,当这些文件编译链接成一个可执行文件时,就会出现大量重定义的错误。在头文件中实用#ifndef
#define #endif能避免头文件的重定义。
举例说明:
例如要编写头文件sad.h
在头文件开头写上两行:
#ifndef _SAD_H
#define _SAD_H//一般是文件名的大写
#endif
头文件结尾写上一行:#endif这样一个工程文件里同时包含两个sad.h时,就不会出现重定义的错误了。
分析:第一次包含sad.h时,由于没有定义_SAD_H,条件为真,这样就会包含(执行
)#ifndef _SAD_H和#endif之间的代码,当第二次包含sad.h时前面一次已经定义了_SAD_H,条件为假,#ifndef _SAD_H和#endif之间的代码也就不会再次被包含,这样就避
了重定义了。主要用于防止重复定义宏和重复包含头文件
2、#include <filename.h> 和 #include “filename.h” 有什么区别?
对于#include<filename.h>,编译器先从标准库路径开始搜索filename.h,使得系统文件调用比较快;
对于#include"filename.h",编译器先从用户的工作路径开始搜索filename.h,后去寻找系统路径,使得自定义文件较快。所以在写代码的过程中要根据实际情况选择是<>还是""
引申:头文件的作用有哪些?头文件的作用主要表现为以下两个方面:
(1)**通过头文件来调用库功能**。出于对源代码保密的考虑,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口是怎么实现的,编译器会从库中提取相应的代码;
(2)**头文件能加强类型安全检查**。如果某个接口被实现或被调用时,其方式与头文件中的声明不一致,编译器就会指出错误,能大大减轻程序员调试、改错的负担。
3、const 有什么用途?(请至少说明两种)
答:(1)可以定义 const 常量
(2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
(1)可以定义const常量,具有不可变性。
例如:const int Max=100; Max++会产生错误;
(2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
例如: void f(const int i) { .........} 编译器就会知道i是一个常量,不允许修改;
(3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!
如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可!
/
//(有错,这样不能修改,会报重复定义的错误)
(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在函数体内修改了i,编译器就会报错;
例如: void f(const int i) { i=10;//error! }
(5) 可以节省空间,避免不必要的内存分配。 例如:
#define PI 3.14159 //常量宏
const double Pi=3.14159; //此时并未将Pi放入RAM中 ......
double i=Pi; //此时为Pi分配内存,以后不再分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存!
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是像#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干份拷贝。
(6) 提高了效率。
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
四、有关内存的思考题(20 分)
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:程序崩溃。
因为GetMemory并不能传递动态内存,
Test函数中的 str一直都是 NULL。
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:可能是乱码。
因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。
Void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:(1)能够输出hello
(2)内存泄漏
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
请问运行Test 函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。
因为free(str);之后,str成为野指针,
if(str != NULL)语句不起作用。