作业整理12.2

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)**头文件能加强类型安全检查**。如果某个接口被实现或被调用时,其方式与头文件中的声明不一致,编译器就会指出错误,能大大减轻程序员调试、改错的负担。

3const 有什么用途?(请至少说明两种)

答:(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)语句不起作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值