Skip to content

C/C++ Notes

My C/C++ Projects

GCC: gcc/g++

GCC 代表 GNU Compiler Collections, 主要用于编译 C/C++,但也支持其它语言,如 Fortran, Go.

  • gcc/g++ 都可以编译任何 .c 或者 .cpp 文件,但 gcc 将其看作分别 C, C++,而 g++ 将其都看成 C++
  • 在 link 时,g++ 会自动连接 C++ 标准库,而 gcc 不会

see also:

一步到位的编译命令为

gcc test.c -o test

它可以拆解为以下若干步

# 预处理, -E 使得编译器在预处理后停止,并输出预处理结果
gcc -E test.c -o test.i
// gcc -E test.c
# 编译为汇编代码, -S 程序编译期间,生成汇编代码后,停止
gcc -S test.i -o test.s
# 汇编
gcc -c test.s -o test.o
# 连接
gcc test.o -o test
  • 多个程序文件的编译
gcc test1.c test2.c -o test 
  • 检错
gcc -pedantic illcode.c -o illcode
gcc -Wall illcode.c -o illcode
gcc -Werror test.c -o test
  • 编译成可执行文件
gcc -c -I /usr/dev/mysql/include test.c -o test.o
  • 连接库文件
gcc -L /usr/dev/mysql/lib –lmysqlclient test.o -o test
  • 强制连接时使用静态链接库: 优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,-static 强制使用静态链接库

/usr/dev/mysql/lib 目录下有链接时所需要的库文件libmysqlclient.solibmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令

gcc -L /usr/dev/mysql/lib -static -lmysqlclient test.o -o test
  • 静态库链接时搜索路径顺序:

    1. ld会去找GCC命令中的参数-L
    2. 再找gcc的环境变量 LIBRARY_PATH
    3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
  • 动态链接时、执行时搜索路径顺序: see also

    1. 编译目标代码时指定的动态库搜索路径
    2. 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径
    3. 配置文件 /etc/ld.so.conf 中指定的动态库搜索路径
    4. 默认的动态库搜索路径 /lib
    5. 默认的动态库搜索路径 /usr/lib

参考 Linux GCC常用命令

(n+3)&~3 used for memory alignment

The case comes from 🔗 For example, when n=14, then the answer is 16 because - 17 is represented in binary as 10001 - 3 is represented in binary as 00011 then 17 AND ~3 gives 10000, which is 16 in decimal.

source: https://www.cplusplus.com/reference/cstdio/printf/

GSL

  • My compiled lib and include for Windows and the detailed procedures: szcf-weiya/GSLwin

OpenMP

get its version

// https://stackoverflow.com/questions/1304363/how-to-check-the-version-of-openmp-on-linux
#include <unordered_map>
#include <iostream>
#include <omp.h>
using namespace std;

int main()
{
    unordered_map<unsigned, string> map{{200505,"2.5"},{200805,"3.0"},{201107,"3.1"},{201307,"4.0"},{201511,"4.5"},{201811,"5.0"},{202011,"5.1"}};
    cout << "We have OpenMP " << map.at(_OPENMP) << endl;
}

On T460P,

$ g++ version.cpp -fopenmp
$ ./a.out 
We have OpenMP 4.5

data scope attribute clause

image

source: Shared and private variables in a parallel environment

#pragma omp critical

Info

A practical example: r.push_back(j);

指定某个区域的代码,每次只能同时被一个线程执行。

references

OpenMP topic: Loop parallelism

OpenMP并行构造的schedule子句详解

Pointer

reference vs pointer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <iostream>
using namespace std;

int main()
{
    int array[3] = {1, 2, 3};
    // int &b = array; // WRONG!!
    int &b = *array; // left-hand side is `Int`, then right-hand side should also be int
    int c = *array;
    cout << array[0] << endl;
    cout << &array[0] << endl;
    cout << &b << endl;
    cout << &c << endl;
}
  • 赋值时左右端类型需相同,与符号 *& 无关
  • barray 的地址相同

参考 c++指针(六)——指针与引用_大风车-CSDN博客

输出字符型指针地址值

C 语言中通过 printf 便可以很方便地输出字符串指针地址值,如

#include <stdio.h>
int main (){
    const char *pstr = "hello world";

    printf("字符串: %s\n", pstr);
    printf("字符串起始地址值: %p\n", pstr);

    return 0;
}

运行结果为,

$ gcc print_pointer_address.c
$ ./a.out 
字符串: hello world
字符串起始地址值: 0x56499eb0e724

但在 Cpp 中没那么简单,主要原因是

C++标准库中I/O类对 << 操作符重载,因此在遇到字符型指针时会将其当作字符串名来处理,输出指针所指的字符串

类似 C 中强制类型转换,用 static_cast 将字符串指针转换成无类型指针。

#include <iostream>
using namespace std;

int main()
{
    const char *pstr = "hello world";
    cout << pstr << endl;
    cout << static_cast<const void*>(pstr) << endl;
    return 0;
}

运行结果为,

$ g++ print_pointer_address.cpp 
$ ./a.out 
hello world
0x556f1462fa65

参考 C++中cout输出字符型指针地址值的方法

指针初始化

double x;
double *p = &x;

DO NOT

double *p = 5;

BUT

double *p = "aaa";
并且要初始化,不能

double *p;
然后直接传参了,这是不对的。

参数和返回值的三种传递方式

参考

值传递

改变 x 的值不会影响 n

void Func1(int x)
{
    x = x + 10;
}
int n = 0;
Func1(n);

指针传递

void Func2(int *x)
{
    (*x) = (*x) + 10;
}
int n = 0;
Func2(&n);

引用传递

xn 是一个东西

void Func3(int &x)
{
    x = x + 10;
}
int x = 0;
Func3(n);

引用传递的性质像指针传递,而书写方式像值传递。

int m;
int &n = m;

其中 nm 的一个引用 (reference),而 m 是被引用物 (referent).

引用的规则如下:

  • 引用被创建时同时被初始化,而指针则可以在任何时候初始化;
  • 不能有 NULL 引用,必须与合法的存储单元关联,而指针可以是 NULL;
  • 一旦引用被初始化,就不能改变引用的关系,而指针则可以随时改变所指的对象。

多线程编程(未完)

参考 c++多线程编程_狂奔的蜗牛-CSDN博客

say_hello

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

// 函数指针
void* say_hello(void *args)
{
  cout << "hello ..." << endl;
}

int main(int argc, char const *argv[]) {
  pthread_t tids[NUM_THREADS];
  for (int i = 0; i < NUM_THREADS; i++)
  {
    // 线程的id,线程参数, 线程运行函数的起始地址,运行函数的参数
    int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
    if (ret!=0)
    {
      cout << "pthread_create error: error_code = " << ret << endl;
    }
  }
  pthread_exit(NULL);
  return 0;
}

两次运行的结果混乱,因为没有同步?

say_hello_paras

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

// 函数指针
void* say_hello(void *args)
{
  int i = *((int*)args);
  cout << "hello in " << i << endl;
}

int main(int argc, char const *argv[]) {
  pthread_t tids[NUM_THREADS];
  cout << "hello in main" << endl;
  for (int i = 0; i < NUM_THREADS; i++)
  {
    // 线程的id,线程参数, 线程运行函数的起始地址,运行函数的参数
    int ret = pthread_create(&tids[i], NULL, say_hello, (void*)&i);
    // &i表示取i的地址,即指向i的指针
    cout << "current pthread id = " << tids[i] << endl;
    if (ret!=0)
    {
      cout << "pthread_create error: error_code = " << ret << endl;
    }
  }
  pthread_exit(NULL);
  return 0;
}

结果混乱!!

可能原因:主进程还没开始对i赋值,线程已经开始跑了…?

say_hello_paras_revise

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

// 函数指针
void* say_hello(void *args)
{
  int i = *((int*)args);
  cout << "hello in " << i << endl;
}

int main(int argc, char const *argv[]) {
  pthread_t tids[NUM_THREADS];
  int indexes[NUM_THREADS]; //保存i的值避免被修改
  cout << "hello in main" << endl;
  for (int i = 0; i < NUM_THREADS; i++)
  {
    indexes[i] = i;
    // 线程的id,线程参数, 线程运行函数的起始地址,运行函数的参数
    int ret = pthread_create(&tids[i], NULL, say_hello, (void*)&(indexes[i]));
    // &i表示取i的地址,即指向i的指针
    //cout << "current pthread id = " << tids[i] << endl;
    if (ret!=0)
    {
      cout << "pthread_create error: error_code = " << ret << endl;
    }
  }
  //pthread_exit(NULL);
  for (int i = 0; i < NUM_THREADS; i++)
    pthread_join(tids[i], NULL); //用于等待一个线程的结束
  return 0;
}

Algorithms

const 总结

ref

new

申请空间,并执行相应的构造函数

delete

执行析构函数,并释放空间

引用的本质是指针常量

const int m;
//int* p = &m;//wrong
const int* p = &m;
int *const pc = &m; //必须初始化,引用的本质

构造函数析构函数作用区间

A a;
A *ap;
if (...)
{
    B b;
    ...// B析构
    ap = new A;
}
......// A析构
delete ap;

先执行基类构造函数,再派生类构造函数; 先执行派生类析构函数,再派生基类析构函数。

函数模板和模板函数

ref

C++中,函数模板与同名的非模板函数重载时,应遵循下列调用原则: 1. 寻找一个参数完全匹配的函数,若找到就调用它。若参数完全匹配的函数多于一个,则这个调用是一个错误的调用。 2. 寻找一个函数模板,若找到就将其实例化生成一个匹配的模板函数并调用它。 3. 若上面两条都失败,则使用函数重载的方法,通过类型转换产生参数匹配,若找到就调用它。 4. 若上面三条都失败,还没有找都匹配的函数,则这个调用是一个错误的调用。

初始化列表

ref

extern

reference

在Rcpp中,extern “C” 告诉编译器,保持其名称,不要生成用于链接的中间函数名。

“symbol lookup error”

./test: symbol lookup error: ./test: undefined symbol:

动态链接库的原因,因为更新完gsl之后,原先的动态链接库不管用了,可以用下面的命令追踪动态链接库

ldd test
ldd -d -r test

参考c++ runtime “symbol lookup error”

字符数组与数字互换

http://blog.csdn.net/sunquana/article/details/14645079

字符数字转数字

  1. atoi
  2. atof
  3. atol
  4. strtod
  5. strtol

数字转字符

sprintf

C: Correctly freeing memory of a multi-dimensional array

https://stackoverflow.com/questions/1733881/c-correctly-freeing-memory-of-a-multi-dimensional-array

#pragma unroll的用法

http://blog.csdn.net/fengzizhuang/article/details/9300431

Getting std :: ifstream to handle LF, CR, and CRLF?

  1. https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf

  2. https://stackoverflow.com/questions/9188126/c-strange-behavior-with-stdistream-or-sentry-wrap-around/9189541#9189541

std::istream& safeGetline(std::istream& is, std::string& t)
{
    t.clear();

    // The characters in the stream are read one-by-one using a std::streambuf.
    // That is faster than reading them one-by-one using the std::istream.
    // Code that uses streambuf this way must be guarded by a sentry object.
    // The sentry object performs various tasks,
    // such as thread synchronization and updating the stream state.

    std::istream::sentry se(is, true);
    std::streambuf* sb = is.rdbuf();

    for(;;) {
        int c = sb->sbumpc();
        switch (c) {
        case '\n':
            return is;
        case '\r':
            if(sb->sgetc() == '\n')
                sb->sbumpc();
            return is;
        case EOF:
            // Also handle the case when the last line has no line ending
            if(t.empty())
                is.setstate(std::ios::eofbit);
            return is;
        default:
            t += (char)c;
        }
    }
}

使用这个代码注意一个问题 应该使用

while(!safeGetline(input, line).eof)

不能用

while(safeGetline(input, line))

fPIC

参考http://blog.sina.com.cn/s/blog_54f82cc201011op1.html

C++ public/protected/private

深入理解C++中public、protected及private用法

C++按行读取文本文件

C++按行读取文本文件

struct–构造函数对结构体初始化的影响

struct–构造函数对结构体初始化的影响

C++11 std::chrono库详解

C++11 std::chrono库详解

vector::erase()方法的详细介绍及问题解答

adapted from vector::erase()方法的详细介绍及问题解答

当调用erase()后Iter迭代器就失效了,变成了一野指针。… 要解决调用erase()方法后,Iter迭代器变成野指针的问题,这个时候呢给他赋一个新的迭代器给他。

// ref: http://blog.sina.com.cn/s/blog_6377b8e60100ino6.html

#include <vector>
#include <iostream>
using namespace std;

int main()
{
  vector <int> v1;
  vector <int>::iterator Iter;
  v1.push_back( 10 );
  v1.push_back( 20 );
  v1.push_back( 30 );
  v1.push_back( 40 );
  v1.push_back( 50 );
  cout << "v1 =" ;
  for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ ) 
    cout << " " << *Iter;
  cout << endl;
  v1.erase( v1.begin( ) );
  cout << "v1 =";
  for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ ) 
    cout << " " << *Iter;
  cout << endl;
  v1.erase( v1.begin( ) + 1, v1.begin( ) + 3 );
  cout << "v1 =";
 for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ ) 
   cout << " " << *Iter;
 cout << endl;
 //当调用erase()后Iter迭代器就失效了,变成了一野指针。
 //所以要处理这种问题,关键是要解决调用erase()方法后,Iter迭代器变成野指针的问题,
 //这个时候呢给他赋一个新的迭代器给他。
 v1.push_back( 10 );
 v1.push_back( 30 );
 v1.push_back( 10 );
 cout << "v1 =";
 for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ ) 
   cout << " " << *Iter;
 cout << endl; 
 for(Iter = v1.begin(); Iter != v1.end(); Iter++) 
   { 
     if(*Iter == 10) 
       { 
     v1.erase(Iter);
     Iter = v1.begin(); //当erase后,旧的容器会被重新整理成一个新的容器
     // or
     // Iter = v1.erase(Iter);
       }
     if (Iter == v1.end())
       break;
   }

 cout << "v1 =";
 for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ ) 
   cout << " " << *Iter;
 cout << endl; 
 // another one
 v1.erase(v1.begin()+1);
 cout << "v1 =";
 for ( Iter = v1.begin( ) ; Iter != v1.end( ) ; Iter++ ) 
   cout << " " << *Iter;
 cout << endl; 

 return 0;

}

最长递增子序列 O(NlogN)算法

最长递增子序列 O(NlogN)算法

c语言中realloc()函数解析

c语言中realloc()函数解析

C语言结构体里的成员数组和指针(关于零数组)

C语言结构体里的成员数组和指针(关于零数组)

C语言:数组和指针的区别

C语言:数组和指针的区别

1017. The Best Peak Shape (35)

1017. The Best Peak Shape (35)

error: ‘stod’ was not declared in this scope

error: ‘stod’ was not declared in this scope

分词

参考The Porter Stemming Algorithm

C++中String类的字符串分割实现

参考C++中String类的字符串分割实现

typeinfo.h

参考C++: typeinfo.h

exit(0), exit(1)和return的区别

参考exit(0)与exit(1)、return区别 - ITtecman - 博客园

  • exit(0): 正常运行程序并退出程序
  • exit(1): 非正常运行导致退出程序
  • return: 返回函数

命令行参数的传入

申明 main 函数如下

int main( int argc, char *argv[] );
int main( int argc, char **argv );

其中 argc 参数包含参数的计数值,而 argv 包含指向这些参数的指针数组,且第一个参数为程序名。比如这里

size_t order = atoi(argv[1]);

对于命令行的处理,可以采用 getopt() 或者 getopt_long().

参考使用 getopt() 进行命令行处理

variably modeified type in C language

参考Variably modified type in C language - Stack Overflow

write into file immediately

fprintf(fileptr, "writing to file\n");
fflush(fileptr);

How to make fprintf() writes immediately

or refer to

Linux实时将所有输出重定向到文件

Map

  • find returns an iterator, so if the key does not exist it returns an end iterator. 🔗
std::map<int, int> some_map;
if (some_map.find(10) != some_map.end())
{
  // key exists
}
else
{
  // key does not exist
}