目录
一,可变长数组
C99开始支持可变长数组:
void testf()
{
int n;
scanf("%d",&n);
int arr[n];
arr[3]=45;
printf("%d",arr[3]);
}
C++不支持,这是唯一C++没有的C语言的语法。
可变长数组只能是局部变量,不能是全局变量。
然而,一些编译器让C++也支持可变长数组,我推测应该是直接按照C语言中的逻辑进行编译。
二,伸缩型数组
C和C++都支持
typedef struct
{
int a;
char data[];
}st;
或者
typedef struct
{
int a;
char data[0];
}st;
第一种写法是C语言标准语法,第二种叫struct hack,不属于标准语法。
数组data是不占内存的,实际能分配多少内存,取决于给这个结构体分配多少内存,data说白了就是指向结构体的尾巴后面一个字节的指针。
相关文章:结构体大小和对齐值_nameofcsdn的博客-CSDN博客
三,常量
1,字面值和只读变量、数组长度
在C++中,常量包括字面值和只读变量:
int a=12345;
const int b=123;
这里,12345是字面值,a是变量,b是只读变量。
12345是常量,b是常量,a不是常量。
在C中,常量只包括字面值,只读变量不是常量。
上述代码在C语言的解读是,12345是常量,b不是常量。
C和C++中,定义数组的大小都只能用常量。
所以C++有2种定义数组的方法:
const int b=123;
int c[123];
int d[b];
这2种写法都是正确的。
但是在C语言中,上述d数组如果是全局变量就是非法的,如果是局部变量那就是可变长数组。
2,const、运行期常量、编译期常量
关键字const叫做限定符,因为它限定了声明的含义。
const变量是区分编译期常量和运行期常量的
const int b=123;
int d[b];
这是的b编译期常量
int f(int a)
{
const int b=a;
int d[b];
cout<<sizeof(d)/sizeof(int);
return 0;
}
这里的b是运行期常量,它的值在编译期是未知的,数组d是可变长数组。
于是我在网上找到了另外一种代码,用array检测一个变量是不是常量:
#include<iostream>
#include <array>
using namespace std;
int main()
{
int a = 10;
const int b = a;
const int c = 10;
//array<int,b>arr;
array<int, c>arr2;
return 0;
}
用运行期常量给array对象分配空间是不行的,必须用编译期常量才行。
总之,const变量是区分编译期常量和运行期常量的,,而字面值当然都是编译期常量。
3,constexpr、常量表达式、CTFE
在上面的例子中,同样是const常量,有的是编译期常量,有的是运行期常量。
于是C++新增了constexpr关键字,对于编译期常量可以直接用constexpr来修饰。
#include<iostream>
#include <array>
using namespace std;
int main()
{
int a = 10;
const int b = a;
constexpr int c = 10;
//array<int,b>arr;
array<int, c>arr2;
return 0;
}
constexpr还可以用于CTFE,即Compile Time Function Execution
先看如下代码:
#include<iostream>
#include <array>
using namespace std;
const int f(int a)
{
return a*a;
}
int main()
{
// array<int,f(5)>arr;
return 0;
}
编译不过是因为,f(5)是运行期常量,而array需要的是编译期常量
把const换成constexpr之后:
#include<iostream>
#include <array>
using namespace std;
constexpr int f(int a)
{
return a*a;
}
int main()
{
array<int,f(5)>arr; //5是字面值
int a=5;
//array<int,f(a)>arr2; //a是变量
const int b=5;
array<int,f(b)>arr3; //b是编译期常量
const int c=a;
//array<int,f(c)>arr4; //c是运行期常量
return 0;
}
这样,5和b是编译期常量,所以f(5)和f(b)是编译期常量,而f(a)和f(c)都不是编译期常量,所以不能用来分配array对象。
总之,constexpr的作用是,在编译期运行函数,在输入编译期常量的条件下,可以返回编译期常量。
四,结构体
1,成员
(1)C++允许有内部成员函数,且允许该函数是虚函数,C的结构体内不允许有函数存在,但是可以有函数指针。所以C的结构体是没有构造函数、析构函数、和this指针的。
(2)C的结构体对内部成员变量的访问权限只能是public,而C++允许public,protected,private三种。
2,继承
C语言的结构体是不可以继承的,C++的结构体是可以从其他的结构体或者类继承过来的。
3,定义方式
(1)C语言定义结构体
(1)不带typedef
一种创建实例的写法:
struct A{
int x;
};
struct A a;
(2)先定义结构体,再typedef
同名定义,两种创建实例的写法:
struct A{
int x;
};
typedef struct A A;
struct A a;
A a2;
不同名定义,两种创建实例的写法:
struct A{
int x;
};
typedef struct A B;
struct A a;
B a2;
(3)typedef 加 结构体定义
同名定义,两种创建实例的写法:
typedef struct A{
int x;
}A;
struct A a;
A a2;
不同名定义,两种创建实例的写法:
typedef struct A{
int x;
}B;
struct A a;
B a2;
本质上,先定义结构体再typedef,和typedef 加 结构体定义,并没有什么区别。
(4)typedef匿名结构体
一种创建实例的写法:
typedef struct{
int x;
}A;
A a;
(2)C++定义结构体
C++除了可以使用上述C语言能使用的所有写法之外,还有3种额外写法:
(1)不带typedef
struct可以省略
struct A{
int x;
};
A a;
(2)结构体定义并创建实例,不同名
struct A{
int x;
}a;
int main()
{
struct A b;
A c;
a.x=12345,b.x=1234,c.x=123;
cout<<a.x<<" "<<b.x<<" "<<c.x;
return 0;
}
输出:12345 1234 123
(3)结构体定义并创建实例,同名
struct A{
int x;
}A;
int main()
{
struct A a;
a.x=12345,A.x=123;
cout<<a.x<<" "<<A.x;
return 0;
}
输出:12345 123
五,auto
C语言中,auto表示自动变量,修饰的是存储类别,参考C语言存储类别、存储区-CSDN博客
C++中,auto表示类型推导,修饰的变量类型。
六,类型转换
1,隐式类型转换
C/C++都有,包括赋值语句转换、初始化时的转换、表达式中的转换、传参时的转换
(1)C语言隐式类型转换
1.当类型转换出现在表达式时,无论是unsigned还是signed的char和short都会被自动转换成int,如有必要会被转换成unsigned int(如果short与int的大小相同,unsigned short就比int大。这种情况下,unsigned short会被转换成unsigned int)。
在K&R那时的C中,float会被自动转换成double(目前的C不是这样)。由于都是从较小类型转换为较大类型,所以这些转换被称为升级。
2.涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
3.类型的级别从高至低依次是long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。
例外的情况是,当long 和 int 的大小相同时,unsigned int比long的级别高。
4.在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能导致类型升级或降级。
5.当作为函数参数传递时,char和short被转换成int,float被转换成double
C语言基本数据类型转换C语言其他基础_c++ opencv读入图像int转double-CSDN博客
(2)C++隐式类型转换
Ranking of implicit conversion sequences
1) Exact match: no conversion required, lvalue-to-rvalue conversion, qualification conversion, function pointer conversion, (since C++17) user-defined conversion of class type to the same class
2) Promotion: integral promotion, floating-point promotion
3) Conversion: integral conversion, floating-point conversion, floating-integral conversion, pointer conversion, pointer-to-member conversion, boolean conversion, user-defined conversion of a derived class to its base
精确匹配、提升、转换 三种隐式类型转换(包括里面的各小项)优先级依次降低。
由构造函数完成的隐式转换:
class A {
public:
A(int x)
{
this->x = x * x;
}
int x;
};
int f(A a)
{
return a.x + 1;
}
int main()
{
cout << f(3) << " ";
return 0;
}
输出10
相当于:
int main()
{
A b = 3;
cout << f(b) << " ";
return 0;
}
而如果是这样:
int f(A a)
{
return a.x + 1;
}
int f(double a)
{
return a + 2;
}
int main()
{
cout << f(3) << " ";
return 0;
}
则输出5,因为int转double的优先级高一些。
2,强制类型转换
C/C++都有,写法略有区别
C风格:(int)x
C++风格:int(x)
by the way, (int)(x) 这种写法也是OK的
3,类型转换去const
c语言的const入参毫无约束力:
int f(const int*pp)
{
int *p=pp;
*p=3;
return 0;
}
C++要稍微好一点:
int f(const int*pp)
{
int *p=(int *)pp;
*p=3;
return 0;
}
4,强制类型转换运算符
C语言里面只有(int)这种形式的强制类型转换运算符,没有专用的强制类型转换运算符。
C++有4个专用的强制类型转换运算符:
dynamic_cast
const_cast
static_cast
reinterpret_cast
发明这4个运算符,是为了严格限制使用场景,使得用法更规范,避免一些不容易发现的错误。
(1)dynamic_cast
(2)const_cast
(3)static_cast
(4)reinterpret_cast
七,内联函数
1,内联函数
内联函数的声明方式是在函数声明或者实现处用inline修饰
#include<iostream>
using namespace std;
inline int f(int x)
{
return x*x;
}
int main()
{
int x;
cin>>x;
cout<<f(x);
return 0;
}
inline的作用是,建议编译器内联。
如果函数体太复杂,太大,或有递归,或者其他特殊原因,编译器会拒绝内联。
2,内联的优缺点
内联就是把指令展开,这个函数体就不存在了,编译之后没有这个符号了。
优点是执行速度快一些,
缺点是代码内存大一些,而且无法直接对这个函数进行打补丁。
3,C/C++内联
单纯的inline,C/C++的语法和细节应该是一样的,
但是如果内联和static、extern等组合起来就比较复杂,C/C++的细节会有所不同。
八,函数原型
九,字符串
1,字符
类型:char
输入:getchar
输出:putchar
示例:
#include <stdio.h>
void main()
{
char ch;
while (ch = getchar())
{
if (ch == EOF)break;
printf("%d", ch);
putchar(ch);
}
return;
}
运行结果:(8行)
a^Zb
97a
26
a
97a
10
^Z
解释:
输入a^Zb并回车,输入了2个字符a和^Z,再输入a并回车,输入了2个字符a和换行符,再输入^Z程序结束
注1:
'\0'的ASCII码是0,‘^A’ - '^Z' 的ASCII码是1-26,其中换行符'\n'的是10,'^Z'的是26,但是当输入缓冲区为空时'^Z'出现在'\n'之后时,'^Z'的ASCII码是-1,表示文件结尾end of file
注2:
EOF是常量-1,OJ输入输出经常用EOF来控制。
注3:
10号'\n'和26号'^Z'都是getchar的截断,输入一行字符串,getchar只能读取到第一个'\n'或'^Z',后面如果还有就丢弃
2,C语言字符串
(1)字符串的表示
c语言中,字符串用char数组表示,字符串末尾加上'\0'表示结束
(2)输入输出
输入:gets
输出:puts
示例:
#include <stdio.h>
#include<string.h>
void main()
{
char str[]="abcde";
puts(str);
gets(str);
printf("%d\n", strlen(str));
puts(str);
return;
}
运行结果1:(4行)
abcde
^Za
5
abcde
运行结果2:(5行)
abcde
a^Zb
2
a?
可以看出,换行符和^Z都是gets函数的截断,区别在于
换行符会被gets读取并丢弃,而gets对^Z的处理和getchar一样,缓冲区为空时就是-1号字符,缓冲区不为空时就是26号字符
(3)常用函数
(1)strcat函数——字符串连接
string catenate字符串连接
示例:
#include <stdio.h>
#include<string.h>
void main()
{
char str1[10] = "ab";
char str2[] = "cd";
printf("%d %d\n", strlen(str1), strlen(str2));
strcat(str1, str2);
printf("%d %d\n", strlen(str1), strlen(str2));
printf("%s %s", str1, str2);
return;
}
运行结果:
2 2
4 2
abcd cd
strcat(str1, str2)函数就是把str2复制到str1后面
(2)strcpy和strncpy函数——字符串复制
strcpy(str1,str2)把str2复制到str1的位置
strncpy(str1,str2,n)把str2的前n个字符复制到str1的位置
(3)strcmp函数——字符串比较
strcmp(str1,str2)是按照字典序比较,返回值>0,=0,<0分别表示str1>str2,str1==str2,str1<str2
(4)strlen和sizeof——字符串长度
sizeof是关键字,计算的是数组长度
strlen是函数,计算的是字符串中字符数目,不包括'\0'
(5)strlwr和strupr函数——字符串大小写
strlwr是把大写字母换成小写字母
strupr是把小写字母换成大写字母
总结:C-风格字符串输入——cin或者getline或者get,cin是以空格或换行结束,getline和get都是读取一行。
getline函数会读取并丢弃换行符,而get函数如果带参会保留换行符在输入缓冲区,get函数如果不带参可以读取并丢弃换行符。
3,string类
(1)定义,初始化,输入输出
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str="abc";
cin>>str;
cout<<str;
return 0;
}
(2)string的赋值、计算长度、翻转
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str1="abc";
string str2=str1;
reverse(str2.begin(),str2.end());
cout<<str1<<" "<<str1.length()<<" "<<str2;
return 0;
}
(3)从string中取字符
可以用数组形式,也可以用迭代器形式
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str="abcde";
cout<<str[2];
string::iterator it=str.begin();
cout<<*it;
it=str.end()-1;
cout<<*it;
return 0;
}
输出:
cae
(4)string的比较、连接
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str1="abc",str2="def",str3="def";
if(str1<str2)cout<<"str1<str2\n";
if(str2==str3)cout<<"str2=str3\n";
cout<<str1+str2;
return 0;
}
输出:
str1<str2
str2=str3
abcdef
(5)string转字符数组
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str="abc";
const char *p1=str.c_str();
char *p2=new char[20];
strcpy(p2,p1);
cout<<p2;
return 0;
}
(6)string中查找字符
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str="abca";
cout<<str.find('a')<<" "<<str.find('a',2);
return 0;
}
输出:
0 3
附上c++课本上的string常用函数: