C语言面试笔试题最实用基础总结

一,环境准备

由于不确定到时候是对方共享桌面给你操作,还是你共享桌面演示给他看,通常你本地要准备一个可以写代码的环境。由于我们写代码时候可以本地搜索资料,一般是我们共享自己桌面给对方的情况比较多。

可以将部分常用算法、函数与头文件打印出来或者写在本子上,粘在屏幕旁边。如果平时不怎么写C语言代码,那么总会有一些函数或者头文件记不住的。又或者写了makefile有些地方写错了,一时半会又没看到哪里写错了。排查这些错误虽然没有太大难度,但是时间是宝贵的。纸质小抄虽然有些投机取巧,也相当于给自己吃了一颗定心丸,心里暗示作用。

二,编译

通常自测敲如下两个命令即可测试:

gcc test.c
./a.out

但是有时候对方可能让你写个Makefile。如果没准备好写Makefile, 直接默写下面的通用Makefile,保存成一个Makefile文件放在根目录。下面的Makefile, .c文件放在src文件夹,.h文件放在inc文件夹。可再根据实际情况删减:


LOCAL_PATH := $(shell pwd)
LOCAL_MODULE := module_name
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/src/*.c)
LOCAL_INC_DIRS += -I $(LOCAL_PATH)/inc
LOCAL_C_FLAGS += -O0 -g -fsanitize=address
LOCAL_LINK_MODULES:=  #-pthread
LOCAL_LD_FLAGS += # -L../out/lib/
MAKE := gcc
OBJ:=$(patsubst %.c, %.o, $(LOCAL_SRC_FILES))

MAKE_ARGUMENTS := $(LOCAL_INC_DIRS) $(LOCAL_C_FLAGS)
LINK_ARGUMENTS := $(LOCAL_C_FLAGS) $(LOCAL_LD_FLAGS) $(LOCAL_LINK_MODULES)
 
$(LOCAL_MODULE):$(OBJ)
        $(MAKE) $(LINK_ARGUMENTS) -o $(LOCAL_MODULE) $(OBJ)
%.o:%.c
        $(MAKE) $(MAKE_ARGUMENTS) -c -o $@ $<

.PHONY:clean
clean:
        rm -f $(OBJ)
        rm -f $(LOCAL_MODULE)

三,输入输出

写好了代码,编译通过,就需要测试自己的代码了。有时候考官可能让你自己测试几个边界值。

常见的输入方法,可以是通过main()函数的参数列表传入:

int main(int argc, char** argv) {
    if(argc > 2) {
        printf("arg[1]=%s\n", arg[1]);
        printf("arg[2]=%s\n", arg[2]);
    }
    return 0;
}

argv[0]是自己的程序名,后面是参数列表。之后可以通过对参数的字符串处理即可。

如果要把十进制字符串转化为整数,可以用如下函数:

#include <stdlib.h> 

int i = atoi("122");

 但是这个只能转换十进制的数字。如果有些算法需要输入十六进制的数字,则没法用atoi()。对于十六进制数字情况,可以通过scanf()函数输入。那么程序就不能用main()函数的参数作为输入了。可以写一个循环,不停地手动输入,然后计算后再输出。

#include <inttypes.h> //SCNx64 PRIx64 
#include <stdio.h>
#include <stdlib.h> //atoi()

int main(int argc, char** argv) {
    uint64_t num = 0xffffffff00000000, ret;
    int x, y, temp;
    char str[32] = {0};

    while(EOF != scanf("%" SCNx64 "%u%u", &num, &x, &y)) {
        sprintf(str, "%ld" , num);
        temp = atoi(str);
        printf("str=%s, temp=%d\n", str, temp);
        ret = swap_bit(num, x, y);
        printf("after swapping bits, result=%" PRIx64 "\n", ret);
        if(0 == num) break;
    }
    return 0;
}

注意 while(EOF != scanf("%" SCNx64 "%u%u", &num, &x, &y)) 这里面的宏SCNx64, 具体可以其他地方搜索,不再介绍。另外,建议一次性连在一起输入多个参数,中间前后不要有空格回车等分隔符。如下可能会有问题:

 while(EOF != scanf(""%u %u\n", &x, &y)) {...}

 scanf()输入里面包含空格与回车,意味着你可能要多敲空格跟回车才能正常运行。稍不留神输入错误验证出错,自己又不知道怎么回事。

三,常用头文件与函数

从上面代码,我们想到一个场景可能还不够,那就是输出一个十六进制字符串,如何转换成整形。atoi()只能是十进制数字符串转整形。针对十六进制转整形,可能要自己写了。参考如下:

#include <ctype.h>

static int to_Hex(const char *str){
    int sum = 0;
    while (isxdigit(*str)) {
        if (isdigit(*str)){
            sum = sum * 16 + *str - '0';
        }else{
            tolower(*str);
            sum = sum * 16 + *str - 'a' + 10;
        }
        str++;
    }
    return sum;
}

static int to_Oct(const char* str) {
	int sum = 0;
	while (isdigit(*str) && *str != '8' && *str != '9') {
		sum = sum * 8 + *str - '0';
		str++;
	}
	return sum;
}

字符串判断是16进制/8进制/10进制:


int My_atoi(const char *str) {
	int index = 1;
	int sum = 0;
	while (isspace(*str)) {
		str++;
	}
	if (*str == '-') {
		index = -index;
		str++;
	}
	if (*str == '+') {
		str++;
	}
	if (*str == '0') {
		if (*(str + 1) == 'x' || *(str + 1) == 'X') {
			sum = to_Hex(str + 2);
		} else {
			sum = to_Oct(str + 1);
		}
	} else {
		sum = atoi(str);
	}
	return sum * index;
}

四,快速调试

编译时候,可以加上:

-O0 -g -fsanitize=address

当代码出现内存错误时候,不会只是显示一个错误,而是会被ASAN工具打印错误栈。这样方便自己定位代码问题,非常有用。关于ASAN可以找资料,这里不做展开。

五,常用的算法

1, 高通笔试题。写一个客制化的malloc() 与free() 函数,要求malloc()返回的地址是2^n 字节对齐的。

#include <stdlib.h>
#include <inttypes.h>

void* align_malloc(unsigned int target_size, unsigned int align_size) {
    void* tmp_ptr = NULL;
    int64_t ptr_value;

    if((align_size < 8) || ((align_size & (align_size -1)) > 0) || (align_size > 4096)) {
        printf("invalid align size: %u\n", align_size);
        return NULL;
    }

    tmp_ptr = malloc(target_size + align_size + sizeof (void*));
    if (NULL == tmp_ptr) {
        printf("malloc %u bytes failed.\n", target_size);
        return NULL;
    }
    ptr_value = ((int64_t)(tmp_ptr + align_size));
    ptr_value &= ~((int64_t)(align_size - 1));
    printf("addr=%p, align(%u) addr=%p\n", tmp_ptr, align_size, (void*)ptr_value);
    *((void**)ptr_value - 1) = tmp_ptr;
    return (void*)ptr_value;
}

void align_free(void* ptr) {
  void** tmp_ptr;
  tmp_ptr = ((void**)ptr - 1);
  printf("free:%p\n", *tmp_ptr);
  free(*tmp_ptr);
}

int main(int argc, char** argv){
    void* ptr = NULL;
    uint32_t malloc_size = 0, align_size = 0;

    if(argc > 2) {
        malloc_size = atoi(argv[1]);
        align_size = atoi(argv[2]);
    }

    ptr = align_malloc(malloc_size, align_size);
    if(NULL != ptr) {
        align_free(ptr);
    }
}

2, 排序

排序是最常见的算法。当然,有时候可能你写一起其他的算法,没法避免的要用到排序。对于C++ 可以使用如下函数排序:

#include<algorithm>
 
vector<short> nums;

sort(nums.begin(), nums.end());
sort(nums.rbegin(), nums.rend()); //反向排序

对于C语言,至少要学会一种,比如冒泡算法。其他算法如果能记住最好。如果面试官问你为什么选择用冒泡排序,你除了说算法简单以外,还可以说冒泡排序内存占用最少。参考冒泡排序算法:

#include <stdio.h>
#include <stdlib.h>
typedef int TYPE;

typedef enum  {
    false = 0,
    true = 1
} bool;

static void _swap (TYPE* a, TYPE* b) {
    TYPE temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

static void sort(TYPE count, TYPE * const array){
    bool isContinue = false;
    int i, j;

    for (i = 0; i < count; i ++) {
        for(j = 0; j < count - 1; j++) {
            if(array[j] > array[j + 1]) {
                swap(&array[j], &array[j+1]);
                isContinue = true;
            }
        }
        if(!isContinue) {
            printf("sort size %d array completed. i=%d, j=%d\n",count, i, j);
            break;
        }
        isContinue = false;
    }
}

static void print_array(int count, const TYPE * array) {
    int i = 0;
    for(; i < count; i ++) {
        printf("%d ", array[i]);
    }
    printf("\n");
}

int main(int argc, char** argv){
    TYPE data[] = {12, 24, 25, 55, 32, 80, 32,89,90};
    int count = 0;
    count = sizeof(data) / sizeof(data[0]);
    print_array(count, data);
    sort(count, data);
    print_array(count, data);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值