1.用宏定义写swap(x,y)函数
#define swap(x,y) {x=x+y;y=x-y;x=x-y;} //不是特别严谨。
2.什么是预编译,何时需要预编译?
预编译是在函数执行前的预处理工作,以#号开始,不以分号结束,不是C语言的程序语句。何时需要:为了增加程序可读性,进行的名字替换;不需要经常改动的代码体;经常被使用的函数模块和文件。
3.char * const p;charconst *p;constchar *p 有什么区别?
char *const p是常量指针,指针p的值不可修改,指向char类型的数据;
charconst *p指向常量的指针,指针p的值可变,指向的数据为charconst类型,不可变。
const char *p 和charconst *p一样。
4.解释下列输出结果:
char str1[] ="abc";
char str2[] ="abc";
constchar str3[] ="abc";
constchar str4[] ="abc";
constchar *str5 ="abc";
constchar *str6 ="abc";
char *str7 ="abc";
char *str8 ="abc";
printf("%d,%d,%d,%d\n",str1==str2,str3==str4,str5==str6,str7==str8);
0,0,1,1
str1,str2,str3,str4为数组变量,在内存空间中会独立的开辟存储空间,内存地址各不相同;
str5,str6,str7,str8为指向字符常量的指针,内存地址都指向同一个字符常量“abc”,内存地址相同;
5.sizeof()的用法,指针和数组名的使用?
#include<stdio.h>
void uppercase(char str[])
{
printf("函数内%d\n",sizeof(str));
}
int main()
{
char str[] ="ab";
printf("函数外%d\n",sizeof(str));
uppercase(str);
}
//函数外3,函数内str代表的是数组
//函数内8,函数外str代表的是指针,数组名作为函数参数使用时退化为指针。
6.指出下列代码的输出结果。
#include<stdio.h>
int main()
{
int a[5]={1,2,3,4,5};
int *p= (int *)(&a+1);
printf("%d,%d\n",*(a+1),*(p-1));
//printf("%d,%d,%d,%d,%d,%d\n",&a[0],&a[1],&a[2],&a[3],&a[4],&a+1);
}
输出2,5
//*(a+1)就是a[1]
//&a+1是地址偏移一个a数组的大小,这个概念至关重要。&a[0]+1是地址偏移一个a[0]大小的位置,&a+1是地址偏移一个a[]数组
7.下列代码存在什么问题
a.
#include<stdio.h>
int main()
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return0;
}
没有为str分配内存空间,可以正确显示结果,但是存在安全问题。会存在越界进行内存读写导致程序崩溃。
b.
#include<stdio.h>
int main()
{
char *s="AAA";
printf("%s\n",s);
s[0]='B';
printf("%s\n",s);
}
指针s指向字符常量,声明时应该用const char *来定义声明,而且字符常量是不可修改的。
8.写一个标准宏,输入两个参数,并输出较小的那个
#define min(x,y) ((x)>(y)?(y):(x))
9.C和C++中struct的区别是什么?
C中的struct不可以含有成员函数,C++中可以。
C++中struct和class的区别,在于默认的存取权限不同,struct默认为public,class默认为private
10.程序存在的问题?
#include <stdio.h>
#include <stdlib.h>
void getmemory(char *p)
{
p=(char *) malloc(100);
}
int main()
{
char *str=NULL;
getmemory(str); //不能修改实参str的值,str仍然为0
strcpy(str,"hello world");
printf("%s/n",str);
free(str);
return 0;
}
11.程序存在的问题
int main()
{
char str[10];
strcpy(str, "0123456789");
printf("%s\n",str);
return 0;
}
内存越界,字符串长度为11,超过了str的长度。可以正常显示,但是会覆盖掉其他的数据,很危险。
12.C语言指针
int *p[n] 指针数组,数组中每个元素都是指向int数据的指针;
int (*p)[n] 二级指针,p是指向一维数组的指针,这个一维数组有n个int数据变量;
int *p() 返回指针的函数;
int (*p)() 指向函数的指针;
13.数组内存越界
#define MAX 255
int main()
{
unsignedchar A[MAX],i;
for (i=0;i<=MAX;i++) A=i;
}
内存越界,会导致无限循环
14.memset,memcpy,strcpy的区别?
memset 将一段内存中,填充给定的值,通常用于内存初始化,是对较大结构体和数组清零最快的一种方式;
memcpy 将内存中的一段数据拷贝到另一个位置;
strcpy 只适合拷贝字符串,遇到'\0’就结束;
15.不用库函数实现strcpy函数
#include <stdio.h>
#include <assert.h>
char *strcpy(char *strDest,constchar *strSrc)
{
assert(strDest!=NULL && strSrc!=NULL);
char *address = strDest;
while ((*strDest++ = *strSrc++) !='\0') {
;
}
return address;
}
int main()
{
char str[10];
strcpy(str,"012345678");
printf("%s\n",str);
return0;
}
几点说明:
1.注意编程风格,使用strDest, strSrc这样增强可读性的名字。
2.使用断言来检验输入参数的有效性,如果没有对传入参数strDest和strSrc进行检查,一但它们中有一个为NULL,立死! assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回false,则终止程序执行。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。
3.使用const来约束strSrc,提高程序的健壮性。如果函数体内的语句试图改动strSrc的内容,编译器将指出错误。
4.strcpy能把strSrc的内容复制到strDest;为什么还需要char *类型的返回值?
这是为了实现链式表达。增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想
16.ASSERT()是干什么用的?
答:ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。例如,变量n在程序中不应该为0,如果为0可能导致错误,你可以这样写程序: ...... ASSERT( n != 0); k = 10/ n; ...... ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。 assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中。
17.Debug和Release版本的区别?
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
Debug 和 Release 的真正区别,在于一组编译选项。
Debug 版本
参数 含义
/MDd /MLd 或 /MTd 使用 Debug runtime library(调试版本的运行时刻函数库)
/Od 关闭优化开关
/D "_DEBUG" 相当于 #define _DEBUG,打开编译调试代码开关(主要针对assert函数)
/ZI
创建 Edit and continue(编辑继续)数据库,这样在调试过程中如果修改了源代码不需重新编译
GZ 可以帮助捕获内存错误
Release 版本 参数含义
/MD /ML 或 /MT 使用发布版本的运行时刻函数库
/O1 或 /O2 优化开关,使程序最小或最快
/D "NDEBUG" 关闭条件编译调试代码开关(即不编译assert函数)
/GF 合并重复的字符串,并将字符串常量放到只读内存,防止被修改
Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。