1.C 语言中static的作用
/* 1.修饰全局变量,限制其作用域,防止在其他文件单元中被引用。
2.修饰局部变量,限制其生命周期且只被初始化一次,下一次依据上一次的值。
3.修饰函数,限制函数作用域仅限于本文件,而不能被其他文件调用(被static修饰的函数,内存中只有一份,普通函数每次调用都会维持一份拷贝)
*/
2.要对绝对地址0x100000000赋值可以用(unsigned int*)0x10000000 = 1234;那么要让程序跳转到绝对地址
去执行,应该怎么做?
/* 1.强制转换对应地址为函数指针。
(void*())0x10000000;
2.调用
*((void *())0x10000000);
*/
3.不用C库函数比较两个字符串的大小,相等返回0,str1大于str2返回1,str1小于str2返回-1
int myStrCmp(const char *str1, const char *str2){
if(NULL == str1 && NULL == str2){
return 0;
}else if(NULL == str1 && NULL != str2){
return -1;
}else if(NULL != str1 && NULL == str2){
return 1;
}
int len1, len2, cmpLen;
char *p = str1;
char *q = str2;
len1 = 0;
len2 = 0;
cmpLen = 0;
while(*p){
len1++;
p++;
}
while(*q){
len2++;
q++;
}
cmpLen = (len1 < len2)? len1:len2;
p = str1;
q = str2;
while(cmpLen >0){
if(*p > *q){
return 1;
}else if(*p < *q){
return -1;
}else{
p++;
q++;
cmpLen--;
}
}
if(len1 > len2){
return 1;
}else if(len1 < len2){
return -1;
}else{
return 0;
}
}
//chatGPT的答案:
int compare_str(char *str1, char *str2)
{
int i = 0;
while(str1[i] != '\0' && str2[i] != '\0')
{
if(str1[i] > str2[i])
return 1;
else if(str1[i] < str2[i])
return -1;
i++;
}
if(str1[i] == '\0' && str2[i] == '\0')
return 0;
else if(str1[i] == '\0')
return -1;
else
return 1;
}
4.实现一个将字符串逆序的方法,例如原始字符穿"abcd" 逆序后"dcba"
char *reverseStr(char *str){
assert(NULL != str);
char *p = str + strlen(sstr)-1;;
char *q = str;
char temp ;
while(q < p ){
temp = *p;
*p = *q;
*q = temp;
p--;
q++;
}
p = NULL;
q = NULL;
return str;
}
5.以下输出是什么?
#include <stdio.h>
int main(){
char aChar;
int aInt;
aInt = aChar = -120;//右->左
printf("%d\n", aInt);
}
-120
6.memcpy和mmmove的区别
1.memcpy 是memmove的子集
2.memcpy 比memmove快
3.若源地址与目标地址出现重合,memcpy可能出现错误,memmove不会
7.memcpy的实现:dest是目标地址,src是源地址,n是操作的字节数
void *myMemcpy(void *dest, void* src, size_t n){
assert(NULL != dest && NULL != src);
char *p = (char*)(src+n -1);
char *q = (char*)(dest+n-1);
while(n--){
*q-- = *p--;
}
return dest;
}
8.memmove的实现:dest是目标地址,src是源地址,n是操作的字节数
void * memmove(void *dest, void *src, size_t n){
char *d = (char *)dest;
const char *s = (const char*)src;
if(s>d){
while(n--){
*d++ = *s++;
}
}else if(s<d){
d = d+n-1;
s = s+n-1;
while(n--){
*d-- = *s--;
}
}
return dest;
}
9.说明const的使用意义,若错误注明ERR
1.const int *p
2.int const *p
//2同1,表示不能通过指针改变指针指向变量的值
//如:int i = 10; const int *p = &i; *p = 100这个动作将报错
3.int* const p
//表示p的指向不能变
//int i = 10;
//int j = 100;
//int * const p = &i;
//p = &j;此时,若让p再指向j,此处将报错
4.const int* const p
//既无法改变指针指向,也无法改变指针只想的值
10.写出 bool,float,指针变量与"零值"的比较
bool :if(flag)
if(!flag)
float:if((x >= 0.000001)&&(x <= 0.000001)) //x == 0.0
ptr :
if(NULL == ptr)
if(NULL != ptr)
11.以下为Linux64位环境,请计算下列 sizeof()的值
void Func(char str[100]){
请计算:
sizeof(str) = 8;
}
char str[] = "hello";
char *p = str;
int n = 10;
sizeof(str) = 6;//字符数组的大小
sizeof(*p) = 8;//指针8字节
sizeof(n) = 4;//int占4字节
12.const 作用
1.修饰指针变量,const int *p 或者int const *p 表示不能通过指针p修改指向变量的值
2.修饰指针变量,int* const p 表示不能修改指针指向
3.修饰指针变量,const int* const p 指针及其指向的变量均不可修改
4.修饰函数参数,返回值,甚至是函数体,被const修饰的对象将受到强制保护,防止被意外
修改,提高程序的健壮性。
13.volatile 关键字的作用,及应用场景
告诉优化器在用到这个关键字修饰的变量时,必须小心的重新读取这个变量的值,而不是
使用保存在寄存器里的备份
EXP:
多线程应用中被几个任务共享的变量;
并行设备的寄存器
14.在 C++程序中调用被C语言编译后的函数,为什么要加 extern "C" 声明
C++支持函数重载,C不支持,函数在被C++编译后在库中的名字与C语言的
不同,例如:某个函数的原型为void foo(int x, int y)该函数被C编译后
在库中的名字为_foo, 而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了【C链接交换指定符号】extern "C" 来解决名字匹配问题
15.简述下面两个for循环的优缺点
for(i=0; i<N; i++){
if(condition){
doSomethint;
}else{
doOtherthing;
}
}
if(condition){
for(i=0; i<N; i++){
doSomeThing;
}
}else{
for(i=0; i<N; i++){
doOtherThing;
}
}
//第二个判断一次,执行N次效率高, 第一代码简洁
16.有test函数如下
void GetMemory(char *p){
p = (char*)malloc(100);
}
void Test(void){
GetMemory(str);
strcpy(str, "Hello World");
printf(str);
}
请问运行test会有什么结果?
1.调用GetMemory()的时候,传参是str的一个copy副本,所以str并没有被分配内存
2.申请空间没有释放,内存泄露
3.strcpy访问了未申请的空间,可能会输出字符串,但是程序崩溃
17. char *GetMemory(void){
char p[] = "hello world";
return p;
}
void test(void){
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行test会有什么样的结果
//在函数GetMemory运行结束后,其申请的栈空间随即消亡,str指向内存中一段未申请的
//空间,可能打印出字符串,程序可能崩溃
18.void GetMemory(char **p, int num){
*p = (char *)malloc(num);
}
void Test(void){
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
//申请空间未释放,内存泄露,其他没问题19.shell 脚本实现输入年份,并输出其是否是闰年(闰年:可以被4整除却不能被100整除,或者可以被400整除的年份)
#!/bin/bash
input(){
input_var=""
while [ -z $input_var ]
do
read -p "请输入$1" input_var
done
echo $input_var
}
year=$(input 年份)
if [ $[ $year%4 ] -eq 0 -a $[ $year%100] -ne 0 ] || [ $[ $year%400 ] -eq 0 ]
then
echo "$year 是闰年"
else
echo "$year 不是闰年"
fi
20.使用脚本输出以下图形
*
**
***
#!/bin/bash
for((i=1; i<=3; i++))
do
for((j=0; j<i; j++))
do
echo -n "*"
done
echo ""
done
21.如何引用一个已经定义的全局变量
1.可以用引用头文件的方式也可以使用关键字extern
2.使用关键字extern时,如果把变量写错了,编译不会报错,运行时会报错
引用头文件,如果写错变量名,编译时就会报错
22.定义一个宏,返回两个参数中的较小的一个
#define MIN(x,y) (x) < (y)?(x):(y)
23.C 语言中什么样的逻辑是 true
非空
24.简述C语言的内存分布
栈
堆
全局变量区
静态存储区
代码段
25.队列和栈的区别
1.队列先进先出
2.栈,先进后出
26.使用宏定义实现swap(x,y),不使用第三方变量交换两个数的值
#define swap(x,y) do{x=x+y; y=x-y; x=x-y}while(0)
或者
#define swap(s,y) do{x^=y; y^=x; x^=y} while(0)
注意宏定义结尾没有分号
27.计算下面的值
#include <stdio.h>
int main(){
int a,b,c,d;
a = 10;
b = a++;
c = ++a;
d = 10*a++;
printf(a,b,c,d);
return 0;
}
计算输出结果
13,10,12,120
28.设有以下说明和定义(64位系统)
typedef union{
long i;
int k[5];
char c;
}Date;
struct data{
int cat;
Date cow;
double dog;
};
Date max;
printf("%d", sizeof(struct data)+sizeof(max));
sizeof(max)->联合体,大小由最大的成员确定,每次申请8字节,大于20(sizeof(int)*5)且是8的整数倍
的最小数字即24
结构体中嵌套联合体的,等于结构体大小+联合体大小,data结构体中,每次申请8字节,由于联合体嵌套在中间,前后各申请一个8字节的空间即 8+24+8 = 40字节
29.unsigned char *p1;
unsigned char *p2;
p1 = (unsigned char*)0x81000000;
p2 = (unsigned long*)0x83000000;
请问
p1+5 = 0x81000005;
p2+5 = 0x81000028;
不同类型的指针的步长问题。P2的步长是long,8字节,p2+5 一共移动 8*5 即40字节,按16进制表示就是0x28.
30.使用变量a给出以下定义
1.一个指向指针的指针,它指向的指针是指向一个整型数
int **a;
2.一个有10个指针的数组,该指针指向一个整型数的
int * a[10];
3.一个指向10个整型数组的指针
int (*a) [10];
4.一个指向函数的指针,该函数有一个整型参数并返回一个整型值
int (*a) (int);
5.一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型值
int (*a[10])(int);
31.用C语言反转一个单向链表
head----->头指针
struct xxx* prev = NULL;
struct xxx* next = NULL;
struct xxx* reverseLink(struct xxx* head){
if(NULL == head){
return head;
}
while(head != NULL){
next = head->next; //记录下一个节点地址
head->next = prev; //摘下头节点
prev = head; //记录新的头节点
head = next; //指向下一个待摘取节点
}
return prev;
}
32.用C语言写一个函数,判断一个正整数是否是2的整数次幂
if(!(a&(a-1))){
printf("是");
}else{
printf("否")
}
33.解释什么叫死锁及如何避免
1.死锁:两个及以上的进程在执行时,由于竞争资源或者由于彼此通信
而造成的一种阻塞现象。若无外力干涉,它们将无法推进下去,这些
永远相互等待的进程称谓死锁
2.互斥使用,资源只能被一个进程/线程占用
不可剥夺, 资源请求者不能强制从资源占有着手中夺取资源,资源只
能由占用着主动释放。
3.请求和保持,即当前西苑请求者,在请求其他资源的时候,同时保持
对原资源的占用
4.循环等待,即存在一个等待队列,他们相互环形等待
避免:
1.打破互斥
2.改造独占资源为虚拟资源
3.打破不可抢占条件,当已进程占有已独立资源后,又申请一独立资源无法
满足,则退出当前资源占用。
4.检测进程状态
34.下面的进程输出结果是什么
int main(){
int a[5] = {1,2,3,4,5};
int *ptr =(int*)(&a+1);
printf("%d %d\n", *(a+1), *(ptr-1));
return 0;
}
&a 类似一个行指针,类似于int (*ptr)[5] = a;
&a+1 将指针从数组的首地址偏移了 sizeof(a)字节,即此时指向了数组a的末尾
由于ptr是int类型的指针,ptr-1向前偏移4个字节,指向了数组的最后一个元素
即5
a+1 指针偏移sizeof(int)字节,指向第二个元素即 2
所以最终结果 2 5
35.内存对齐问题:
计算下列结构体的大小:
union U1{
long a;
short b;
};
struct S1{
union U1 ua;
int b : 3;
long :0;
char c;
int d;
char buf[];
}
printf("sizeof(struct S1)= [%d]\n", sizeof(struct S1));
sizeof(struct S1) = 24;
union 占8字节
随后(int b )再次申请8字节空间,long : 0;强制从下一个8的整数倍开始对其,所以char c , int d 占用第三个8字节空间,结尾的不定数组不占用空间,共计24字节。(个人见解,欢迎指正)
更多推荐
C语言面试准备(基础知识)
发布评论