一,环境准备
由于不确定到时候是对方共享桌面给你操作,还是你共享桌面演示给他看,通常你本地要准备一个可以写代码的环境。由于我们写代码时候可以本地搜索资料,一般是我们共享自己桌面给对方的情况比较多。
可以将部分常用算法、函数与头文件打印出来或者写在本子上,粘在屏幕旁边。如果平时不怎么写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);
}