c++变量的一些注意点 extern关键字的使用

1.在函数体的外部只能声明变量,或者在声明的时候给变量赋值,不能单独有赋值语句存在。注:在声明的同时赋值是可以的如:int a = 10;

(1)在头文件中先声明变量,再赋值(编译器会报错)

test.h文件:

int a;

a = 10;

(2)在头文件中声明了变量,在cpp文件中的函数外对该变量进行赋值操作赋值操作(编译器会报错)。

test.h文件:

int a;

test.cpp文件:

#include <iostream>

#include "test.h"

a = 10;

int main(){

  std::cout<<a<<std::endl;

}

注:如果在函数体的内部执行赋值语句是可以的。如:int main(){a = 10;std::cout<<a<<std::endl;}

(3)在cpp文件中,在函数的外部有单独的对变量的赋值操作(不是在声明的时候)。(编译器会报错)

test.cpp文件:

#include <iostream>

int a ;

a = 10;

int main(){

  std::cout<<a<<std::endl;

}

结:其实说白了就是在函数的外部不能存在除开声明以外的代码执行语句,不单单是变量的赋值,比如std::cout<<"test"<<std::endl;这样的语句放在函数外部一样编译器会报错。原因就是c++执行代码的时候是通过函数作为入口向下执行的,在函数外部如果有声明语句当然会在真正执行函数中的代码前就执行并分配空间,但是如果有其他执行语句编译器就不知道该何时才去执行那段代码了,所以这样的写法是不允许的。

2.变量的声明,定义和赋值

(1)int a =10;//表示声明了一个int类型的变量a,并且给a变量分配了内存空间,并且给该变量赋一个初始值10。

(2)int a;//表示声明了一个int 类型的变量a,并且给a变量分配了内存空间,并且给a变量赋上了一个默认的初始值,这个初始值在不同的系统中可能不同,一般情况下是0。

(3)int a;a=10;//表示声明了一个int类型的变量a,并且给a变量分配了内存空间,并且给a变量赋上了一个默认的初始值,这个初始值在不同的系统中可能不同,一般情况下是0,然后将a的初始值擦除掉重新赋值为10。

(4)extern int a;//表示声明了一个int类型的变量a。

(5)extern int a;a=10;//报错,因为a变量只是声明了还没有给它定义分配空间,不能执行赋值操作。需要改写为:extern int a ; int a =10;

3.变量的重定义问题

(1)在头文件中定义了变量int a =5;,在cpp文件函数外部再次定义了int a = 10;(全局变量a)。编译器报错,变量重定义。

(2)在头文件中定义了变量int a = 5;,在cpp文件函数内部再次定义了int a = 10;。编译器不会报错,在该函数内部使用变量a的值为10。

(3)在cpp文件函数外部定义了变量int a =5;,在cpp文件函数内部再次定义了变量int a =10;。编译器不会报错,在该函数内部使用变量a的值为10。

(4)如果一个cpp文件引用了两个头文件,这两个头文件中都对变量a进行了定义。编译器报错,变量重定义。

(5)如果一个cpp文件引用了同一个头文件两次,并且这个头文件中对定义了a变量,编译器报错,变量重定义。可以在头文件的第一句加上#pragma once这样保证一个头文件只能被引用一次,或者使用下面#ifndef的方式将重复定义的变量包括进去来保证这段代码只能被引用一次

#ifndef _TEST_H_
#define _TEST_H_
//代码
#endif
(6)在联合多个cpp文件进行编译的时候,如果这些cpp中或者这些cpp引用的头文件中(引用同一个头文件,并且该头文件中做了#pragma once或者#ifndef处理的除外)有定义同样的全局变量,编译器会报错,变量重定义。
 
 
4.使用extern声明变量的用法(下面的说法一般都是用于全局变量的定义,在函数的内部一般不会出现)
(1)一个变量不能被重复定义,但是可以被重复声明。比如:int a;int a;会报错。但是extern int a;extern int a;就不会报错
(2)使用extern关键字声明了一个变量后,在代码中便可以使用该变量进行操作了,就算这个变量没有被定义。在编译的过程中再去其他地方(全部参与编译的文件中)找这个变量的定义,如果找到了这个变量的定义则使用该变量定义的值,如果没有找到则编译器报错。
(3)所以使用extern可以定义一个整个项目的cpp文件*用的全局变量来记录一些数据或者配置。
(4)例:
test.h文件:
extern int a;
test.cpp文件
#include <iostrema>
#include <test.h>
int main(){
  std::cout<<a<<std::endl;
  return 0;
}
test1.cpp文件
int a = 10;
编译:g++ test.cpp test1.cpp -o test
 
5.extern "C"的使用
注意:下面的情况只出现在c++调用已经用gcc编译出来的.o文件里的方法或者在c中调用已经用g++编译出来的.o文件里的方法时会出现
如果是调用不是.o文件中的方法,则不会出现问题:如直接用这个命令一起编译test.cpp 和test1.c:g++ test.cpp test1.c -o test
(1)首先看下面的需求,一个c++的文件需要调用c语言编译出来的库中定义的方法。
test.h文件:
int add(int a,int b);
test.cpp文件:
#include <iostream>
#include <test.h>
int mian(){
  int rel = add(1,2);
  std::cout<< rel<<std::endl;
}
g++编译:g++  -c test.cpp  该编译生成test.o文件
test1.c文件
int add(int a,int b){
  return a+b;
}
gcc编译:gcc -c test1.c  该编译生成文件test1.o文件
将g++编译的test.o文件和gcc编译的test1.o文件联合编译,让test.o调用test1.o中定义的add方法:g++ test.o test1.o -o testapp   报错undefined reference to `add(int, int)'
(2)上面那种混合编译报错的原因是,g++编译器将方法编译后的名字和gcc编译器将方法编译后的名字不同。
由于c++支持方法的多态,所以上面的add方法编译后的名字可能是_add_int_int(只是举个例子实际名称根据不同编译器是不一样的,但是原理差不多),就是方法名加上参数。
而c不支持方法的重载,所以上面的add方法编译后的名字可能是_add。
在用g++联合编译的时候,由于在test.o中只有方法的声明没有实体,所以回去找一个叫_add_int_int的方法实体,但是在test1.o编译后add方法的名字叫_add所以会报错找不到方法。
(3)解决方法是将test.h改为:
extern "C"{
    int add(int a ,int b);
}
(4)之所以用extern "C"就能解决的原因是:用extern "C"声明的代码块内g++编译器就会按照c的方式去编译里面内容,所以add方法编译后的名字就是_add,就可以在test1.o中找到方法的定义了
(5)同样的,gcc编译的c的代码中要调用g++编译的c++的代码中定义的函数也会出现上面的情况,解决的方法是将c++中定义的方法体用extern "C"{}括起来,如下:
test1.cpp:
extern "C"{
int add(int a,int b){
    return a+b;
}
}
编译:g++ -c test.cpp
test.h:
int add(int a ,int b);
test.c:
#include "test.h"
int main(){
    int x = add(1,2);
    printf("结果:%d ",x);
    return 0;
}
编译:gcc -c test1.c
联合编译:gcc test.o test1.o -o testapp