目录

这里记录着一些C语言写程序过程中容易忘记的东西。

持续更新中…
最后更新时间: 2023-5-10

关于时间

使用<sys/time.h>和gettimeofday(struct timeval*, void *)

返回自1970年1月1日UTC时间00:00:00起经过的时间,第一个变量为传入时间的指针,结构为

1
2
3
4
5
6
7
8
9
10
11
struct timeval
{
#ifdef __USE_TIME_BITS64
__time64_t tv_sec; /* Seconds. */
__suseconds64_t tv_usec; /* Microseconds. */
#else
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
#endif
};
#endif

使用<time.h>和clock(void)

返回自程序开始执行以来的时钟滴答数。如果将其除以常数CLOCKS_PER_SEC,将获得程序在CPU中已运行多长时间(以秒为单位)。

代码示例

以1到1000,000,000求和为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//file: tictoc.h
#include <time.h>
#include <sys/time.h>

#ifndef HEADER_FILE_TICTOC
#define HEADER_FILE_TICTOC

clock_t __clock_begin;
struct timeval __time_begin, __time_end;

#define tic __clock_begin=clock()
#define toc if (__clock_begin == -1) printf("toc error!\n"); \
else printf("clock time used: %.3fms\n", (float) (clock() - __clock_begin)/CLOCKS_PER_SEC*1000); __clock_begin=-1

#define timetic gettimeofday(&__time_begin, 0);
#define timetoc gettimeofday(&__time_end, 0); printf("real time used: %.3fms\n", 1000*((__time_end.tv_sec - __time_begin.tv_sec) + (__time_end.tv_usec - __time_begin.tv_usec)*1e-6))

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//file: gettime.c
#include <stdio.h>
#include "tictoc.h"

int main () {
// Start measuring time
tic;
timetic;

long int n = 1000*1000*1000;
long long int sum = 0;
for (int i=0; i<n; sum += ++i);

printf("Result: %lld\n", sum);

// Stop measuring time and calculate the elapsed time
toc;
timetoc;

return 0;
}

函数重载

C语言本身没有函数重载,一种简单的实现办法如下

1
2
3
4
5
6
#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__)

int FOO1(int arg1);
int FOO2(int arg1, int arg2);
int FOO3(int arg1, int arg2, int arg3);

宏替换的结果为

1
2
3
FOO(1) => FOO1(1)
FOO(1,2) => FOO2(1,2)
FOO(1,2,3) => FOO3(1,2,3)

声明优先级

  1. 声明 从它的名字开始读取,然后按照优先级顺序依次读取。
  2. 优先级从高到低依次是:
    1. 声明中被圆括号括起来的那部分
    2. 后缀操作符:
      • 圆括号()表示这是一个函数
      • 方括号[]表示这是一个数组
    3. 前缀操作符:星号*表示“指向…的指针”
  3. 如果constvolatile关键字的后面紧跟类型说明符(如int,long等),那么它作用于类型说明符,在其他情况下,constvolatile关键字作用于它左边紧邻的指针星号。

例子

1
char * const *(*a)();
  1. 先看名字:a,说明这是一个名为a的变量;
  2. 圆括号优先级最高,里面a被星号*修饰,说明变量类型是指针;
  3. 后缀圆括号()优先级比星号*高,说明这是一个函数指针;
  4. 再看(*a)()的前缀星号*,说明这是一个函数指针,所指函数的返回类型为指针;
  5. 再看最左边的类型char * constconst关键字修饰左边的星号*,说明函数返回类型是一个指向常量指针的指针;
  6. 最后看最前面的char,说明该常量指针是一个指向字符的常量指针。

全部连起来就是:声明一个函数指针变量a,所指函数返回一个指向字符型常量指针的指针。


我们再看看著名函数signal()的声明:

1
void (*signal(int sig, void (*func)(int)))(int)
  1. 先看圆括号括起来的部分(*signal(...)),后缀圆括号()比前缀*的优先级高,说明这是一个函数,其返回类型为指针,其名为signal
  2. 再看函数的参数列表(..., ...),说明参数有两个:
    1. 第一个参数是整型,声明为int sig
    2. 第二个参数比较复杂,声明为void (*func)(int),可以看出这是一个指针,指向返回值为void,参数列表包含一个整型。
  3. 我们再来看返回类型的指针所指为何,第一部里的圆括号void (...)(int)外面的后缀圆括号(int)优先级最高,说明返回类型是函数指针,所指函数其参数列表为一个整型,无返回值;

连起来就是:声明一个函数signalsignal返回一个函数指针,这个函数指针指向一个传入一个整型的void类型的函数,signal需要传入两个参数,第一个是整型(信号),第二个与signal的返回类型相同,是指向传入一个整型的void函数的指针。