如果时间充足的话,想学C语言的话推荐去看书《C和指针》,然后配着MOOC上翁恺老师的课程一起看,看完之后基础可以比较扎实,如果时间很紧张就仔细阅读这部分内容,配着翁恺老师MOOC一起。
由于之前有C的基础,这学期也在学习C++(C++可以听翁恺老师和侯捷老师的课,书用C++Prime),所以很多基础的部分就省略掉,只整理一些小点:
(1)cin 和 cout消耗的时间比printf和scanf多得多。
(2)在C++标准中,#include <stdio.h>与#include 写法等价,其它头文件类似。
(3)绝对值在10的9次方范围以内的整数(或称32位整数)都可以用int来存放,如果在10的10次方到10的18次方以内的整数(或称64位整数)用long long型来存放。
(4)遇到浮点型数据都应该用double型来存储。
(5)数字,小写字母,大写字母在ASCII中的顺序是数字<大写字母<小写字母,其中小写字母比大写字母值大32,但大写字母和数字之间还有其它字符。
(6)在编程的时候应该编写后缀为.cpp(DEVC++中)的源文件,也就是说在C++的环境中编程,但整体使用C语言的编程方式,但可以在其中混合使用一些C++的比较方便的东西。
(7)C语言没有bool类型,但C++有,所以可以直接使用bool类型,也可以加上头文件#include <stdbool.h>。
(8)位运算符的优先级没有算术运算符的高。
(9)如果在程序中需要设置一个无穷大的数INF,一般设置成2^30 – 1,具体写法可以是:
两种写法等价。
(10)在使用scanf函数进行读取时,一定要小心%c格式的字符,它是可以读入空格和换行的。使用scanf函数读取字符串%s的时候它是以空格和换行为结束的标志的。
(11)几个有用的输出格式:
(a)%md:输出向右对齐,不足m位的高位补空格,超过m位的原样输出(同理,左对齐m为负数)。
(b)%0md:和上面很相似,不过不足m位的高位补0.
(c)%.mf:保留m位小数,采取原则四舍五入。
(12)getchar()可能会读入空格和换行。
(13)常用math函数:
(a)fabs( double x) : 取绝对值;
(b)floor(double x) 和 ceil( double x) : 向下取整和向上取整;
(c)pow( double r, double p) : 求幂;
(d)sqrt( double x) : 求平方根;
(e)log( double x) : 求对数(以自然数为底);
(f)sin( double x ), cos( double x ), tan( double x ) : 求三角函数(弧度制);
(g)asin( double x ),acos( double x ), atan( double x ) : 反三角函数;
(h)round( double x ) : 四舍五入求整。
(14)冒泡排序(本质在于交换):
书上写法:
void bubble_sort1( int a[], int n )
{
int i, j, temp;
for( i = 1; i < n; i++ )
for( j = 0; j < n - i; j++ )
if( a[j] > a[j + 1] ){
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
我习惯的写法:
void bubble_sort2( int a[], int n )
{
int i, j, temp;
for( i = n - 1; i >= 0; i-- )
for( j = 0; j < i; j++ )
if( a[j] > a[j + 1] ){
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
之所以习惯这种写法是因为之前总是忘掉冒泡排序的算法思想,其实简单来说就是每一次都从头开始遍历,每次选一个当前未排序列表中的最大值放在最后,所以每一次遍历的长度应该减1,所以外循环应该是从末尾开始向前减少,所以每次写冒泡的时候我就能够捋一遍算法的过程。书上的写法其实也很好理解,选择哪种都可以。
(15)如果数组大小较大(大概10的6次方),需要将数组定义在main主函数外面,因为函数内部申请的局部变量是来自系统栈,允许申请的空间较小,会使程序异常退出,而全局变量来自静态存储区,允许申请的空间较大。
(16)memset函数(对数组中每一个元素赋相同的值):
添加头文件<string.h>头文件,函数原型是:
void *memset(void *s, int ch, size_t n)
调用形式是:
void *memset( 数组名, 值, sizeof(数组名) );
由于memset函数是按字节赋值,所以刚开始只使用函数来赋0和-1。
(17)因为gets()函数是以换行作为结束标志的,所以在使用scanf读取完一些内容后再使用gets()时,需要先用getchar()吸收掉换行符。
(18)sscanf和sprintf(均在头文件stdio.h中)
写一段代码示例一下就很好理解了:
//sscanf和sprintf
#include <stdio.h>
int main ( void )
{
char str[100] = "123", str1[100];
int n;
sscanf( str, "%d", &n );
printf( "%d\n", n );
n = 12345678;
sprintf( str1, "%d", n );
printf( "%s\n", str1 );
return 0;
}
运行结果是:
就可以轻松地完成字符串和其它类型数据的转换。
另外,sscanf还支持正则表达式。(正则表达式不太了解,有时间补一下)
(19)指针是一个unsigned类型的整数。
(20)引用:
引用使用符号&,本质是为变量起一个别名(所以常量不能使用引用),引用并不是取地址的意思。
(21)使用构造函数初始化结构体:
同样举例说明:
假设现在创建一个点的结构体:
struct point {
int x;
int y;
};
它会默认生成一个隐藏的构造函数point(){},所以才可以定义结构体变量而不进行初始化,如果想要修改构造函数,比如:
struct point {
int x;
int y;
point( int x_, int y_ ){
x = x_;
y = y_;
}
/*
也可以写为
point( int x_, int y_ ) : x(x_), y(y_){ }
*/
};
这样就不能不经初始化就定义结构变量,不过这样就可以手动进行初始化,例如:
struct point p1 = point( 1, 2 );
(21)C++读入一整行:
char str[100];
cin.getline( str, 100 ); //str是个字符数组
或
string str;
getline( cin, str ); //string容器
C++输出精度控制:
//输出精度
#include <iostream>
#include <iomanip>
using namespace std;
int main ( void )
{
cout << setiosflags( ios::fixed ) << setprecision(2) << 123.456 << endl;
return 0;
}
考试不建议使用cin和cout,容易超时。
(22)浮点数的比较:
由于计算机存储中浮点数总是不确定的,所以在比较两个浮点数时需要用一个精度eps来判断两个浮点数的大小关系,eps合适的值是10的-8次方,同时可以利用宏定义来写判断的语句(注意加括号)。
(23)较高的时间复杂度会让系统返回“运行超时”,对一般的OJ系统来说,一秒钟能承受的最大的运算次数大概是10的7次方~10的8次方,也就是说当算法时间复杂度是O(n^2)的时候,当输入规模n = 1000时,是可以承受的,但n = 100000是不可承受的。(这个问题之前遇到过,在参加2019年3月PAT乙级春考的时候,最后一道题我的算法就是O(n ^2),输入规模n = 100000,最后导致有两个测试点运行超时,扣了9分)。