变量名是由字母和数字组成的序列,但其中第一个字符必须为字母,其中下划线“_”被看做字母,通常用于命名较长的变量名,以提高其可读性。
库例程的名字通常是下划线开头,所以变量名最好不要以下划线开头。大写小写字母是有区别的,它们是不同的名字。通常,在C语言中,变量名使用小写字母,符号常量名全部使用大写字母。
if等关键字必须小写。
数据类型及长度
C语言中提供了下列几种基本数据类型:
名称 | 类型 | 说明 |
---|---|---|
char | 字符型 | 占用一个字节,可以存放本地字符集中的一个字符 |
int | 整型 | 通常反映了所用机器中整数的最自然长度 |
float | 单精度浮点型 |   |
double | 双精度浮点型 |   |
此外,还可以在这些基本数据类型的前面加上一些限定符。short与long两个限定符用于限定整型:
short int sh;
long int counter;
在上述这种类型的声明中,关键字int可以省略。
- short类型通常为16位
- long类型通常为32位
- unsigned char类型变量取值范围为0~255(char对象占8位)
- signed char类型的取值范围则为-128~127(char对象占8位)
- long double类型表示高精度的浮点数。
同整型一样,浮点型的长度也取决于具体的实现,float、double与long double类型可以表示相同的长度,也可以表示两种或三种不同的长度。
Q:编写一个程序以确定分别由signed及unsigned限定的char、short、int与long类型变量的取值范围。采用打印标准头文件中的相应值以及直接计算两种方式实现。后一种方法的实现较困难一些,因为要确定各种浮点类型的取值范围。
A:默认的定义文件在C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\
,比如limits.h。用的是VS 2015版本,直接打开文件可以看到定义,下面贴一下:
//
// limits.h
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The C Standard Library <limits.h> header.
//
#pragma once
#define _INC_LIMITS
#include <vcruntime.h>
_CRT_BEGIN_C_HEADER
#define CHAR_BIT 8 // number of bits in a char
#define SCHAR_MIN (-128) // minimum signed char value
#define SCHAR_MAX 127 // maximum signed char value
#define UCHAR_MAX 0xff // maximum unsigned char value
#ifndef _CHAR_UNSIGNED
#define CHAR_MIN SCHAR_MIN // mimimum char value
#define CHAR_MAX SCHAR_MAX // maximum char value
#else
#define CHAR_MIN 0
#define CHAR_MAX UCHAR_MAX
#endif
#define MB_LEN_MAX 5 // max. # bytes in multibyte char
#define SHRT_MIN (-32768) // minimum (signed) short value
#define SHRT_MAX 32767 // maximum (signed) short value
#define USHRT_MAX 0xffff // maximum unsigned short value
#define INT_MIN (-2147483647 - 1) // minimum (signed) int value
#define INT_MAX 2147483647 // maximum (signed) int value
#define UINT_MAX 0xffffffff // maximum unsigned int value
#define LONG_MIN (-2147483647L - 1) // minimum (signed) long value
#define LONG_MAX 2147483647L // maximum (signed) long value
#define ULONG_MAX 0xffffffffUL // maximum unsigned long value
#define LLONG_MAX 9223372036854775807i64 // maximum signed long long int value
#define LLONG_MIN (-9223372036854775807i64 - 1) // minimum signed long long int value
#define ULLONG_MAX 0xffffffffffffffffui64 // maximum unsigned long long int value
#define _I8_MIN (-127i8 - 1) // minimum signed 8 bit value
#define _I8_MAX 127i8 // maximum signed 8 bit value
#define _UI8_MAX 0xffui8 // maximum unsigned 8 bit value
#define _I16_MIN (-32767i16 - 1) // minimum signed 16 bit value
#define _I16_MAX 32767i16 // maximum signed 16 bit value
#define _UI16_MAX 0xffffui16 // maximum unsigned 16 bit value
#define _I32_MIN (-2147483647i32 - 1) // minimum signed 32 bit value
#define _I32_MAX 2147483647i32 // maximum signed 32 bit value
#define _UI32_MAX 0xffffffffui32 // maximum unsigned 32 bit value
// minimum signed 64 bit value
#define _I64_MIN (-9223372036854775807i64 - 1)
// maximum signed 64 bit value
#define _I64_MAX 9223372036854775807i64
// maximum unsigned 64 bit value
#define _UI64_MAX 0xffffffffffffffffui64
#if _INTEGRAL_MAX_BITS >= 128
// minimum signed 128 bit value
#define _I128_MIN (-170141183460469231731687303715884105727i128 - 1)
// maximum signed 128 bit value
#define _I128_MAX 170141183460469231731687303715884105727i128
// maximum unsigned 128 bit value
#define _UI128_MAX 0xffffffffffffffffffffffffffffffffui128
#endif
#ifndef SIZE_MAX
#ifdef _WIN64
#define SIZE_MAX _UI64_MAX
#else
#define SIZE_MAX UINT_MAX
#endif
#endif
#if __STDC_WANT_SECURE_LIB__
#ifndef RSIZE_MAX
#define RSIZE_MAX (SIZE_MAX >> 1)
#endif
#endif
_CRT_END_C_HEADER
根据:https://junchu25.wordpress.com/2008/02/29/the-c-programming-language-2-1/
先说关于符号和无符号的概念,有符号类型(signed),在二进制表示中,最高位为符号位。比如5(以short为例),二进制表现为0000000000000101,它的最高位是0代表正数,如果是1则代表负数。对于无符号类型,它的高位没有符号标记,所以可以空出一位来表示大一倍的正数。假设int类型可以表示2的31次方,则unsigned int可以表示2的32次方。 2^31 * 2所以是大一倍。
那么对于无符号数,直接给0取反并赋予对应的无符号类型,则可以得到这种无符号类型的最大值,而0均为无符号类型的最小值。
对于有符号数,同上,增加右移1位的操作,然后强制转换为对应的有符号类型即可得到最大值,最小值为负的最大值,直接取负即可。那么代码如下(基本属于copy,注意最后是个无符号类型的应当用%u输出,%d无法输出无符号整数,打印会有问题):
#include <stdio.h>
/*determine ranges of types*/
void main()
{
/* signed ranges of types*/
printf(" signed char min = %d \n", -(char)((unsigned char) ~0 >> 1));
printf(" signed char max = %d \n", (char)((unsigned char) ~0 >> 1));
printf(" signed short min = %d \n", -(short)((unsigned short) ~0 >> 1));
printf(" signed short max = %d \n", (short)((unsigned short) ~0 >> 1));
printf(" signed int min = %d \n", -(int)((unsigned int) ~0 >> 1));
printf(" signed int max = %d \n", (int)((unsigned int) ~0 >> 1));
printf(" signed long min = %d \n", -(long)((unsigned long) ~0 >> 1));
printf(" signed long max = %d \n", (long)((unsigned long) ~0 >> 1));
/*unsigned types*/
printf(" unsigned char max = %u \n",(unsigned char) ~0);
printf(" unsigned int max = %u \n",(unsigned int) ~0);
printf(" unsigned short max = %u \n",(unsigned short) ~0);
printf(" unsigned long max = %u \n",(unsigned long) ~0);
}
执行示例:
>cl demo.c
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.00.24215.1 版
版权所有(C) Microsoft Corporation。保留所有权利。
demo.c
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:demo.exe
demo.obj
>demo.exe
signed char min = -127
signed char max = 127
signed short min = -32767
signed short max = 32767
signed int min = -2147483647
signed int max = 2147483647
signed long min = -2147483647
signed long max = 2147483647
unsigned char max = 255
unsigned int max = 4294967295
unsigned short max = 65535
unsigned long max = 4294967295
某些字符可以通过转义字符序列表示为字符和字符串常量。转义字符序列看起来像两个字符,但只表示一个字符。
另外我们可以用'\ooo'表示任意字节大小的位模式。其中ooo代表1~3个八进制数字(0-7)。这种位模式还可以用'\xhh'表示其中,hh是一个或多个十六进制数字(0-9,a-f,A-F)。因此,我们可以按照下列形式书写语句:
#define VTAB '\013' /* ASCII纵向制表符 */
#define BELL '\007' /* ASCII响铃符 */
十六进制形式:
#define VTAB '\xb' /* ASCII纵向制表符 */
#define BELL '\x7' /* ASCII响铃符 */
ANSI C语言中的全部转移字符序列如下:
\a 响铃符
\b 回退符
\f 换页符
\n 换行符
\r 回车符
\t 横向制表符
\v 纵向制表符
\\ 反斜杠
\? 问号
\' 单引号
\" 双引号
\ooo 八进制数
\xhh 十六进制数
字符常量'\0'
表示值为0的字符,也就是空字符null。通常用'\0'
的形式代替0,以强调某些表达式的字符属性,但其数字值为0。常量表达式是仅仅只包含常量的表达式。这种表达式在编译时求值,而不在运行时求值。它可以出现在常量可以出现的任何位置。
字符串连接可以多个"one" " two"
这样挨着就行(感觉又学到新知识了),代码举例:
#include <stdio.h>
void main()
{
printf("shizsfia" " zheshi0");
}
输出是shizsfia zheshi0
'x'
与"x"
的区别:前者是一个整数(用其他语言多了就会感觉这有点坑),其值是字母x在机器字符集中对应的数值;后者是一个包含一个字符以及一个结束符'\0'
的字符数组。
枚举
enum boolean {NO, YES}
在没有显式说明的情况下,enum类型中的第一个枚举名值为0,第二个为1,第三个为2...如果指定了部分枚举名的值,那么未指定的枚举名的值将依着最后一个指定值向后递增。
Q:在不使用运算符&&或||的条件下编写一个与下面的for循环语句等价的循环语句。
for (i = 0; i < lim -1; && (c = getchar()) != '\n' && c != EOF; ++i)
s[i] = c;
A:代码如下,用的if
i = 0;
if (i < lim -1)
if ((c = getchar()) != '\n')
if (c != EOF;)
++i;
Q:编写函数htoi(s),把由16进制数字组成的字符串(包含可选的前缀0x或0X)转换为与之等价的整型值。字符串中允许包含的数字包括:0~9、a~f以及A~F。
A:一开始不明白,原来是转换十进制?。。。代码并非直接写的,只是添加了一点注释。
#include <stdio.h>
int htoi(char s[]);
main()
{
char s1[] = "10";
char s2[] = "2D";
char s3[] = "3f";
char s4[] = "0X4F";
char s5[] = "0x3a";
printf("%s -> %d\n", s1, htoi(s1));
printf("%s -> %d\n", s2, htoi(s2));
printf("%s -> %d\n", s3, htoi(s3));
printf("%s -> %d\n", s4, htoi(s4));
printf("%s -> %d\n", s5, htoi(s5));
}
int htoi(char s[])
{
int n = 0;
int i = -1;
while (s[++i] != '\0') {
if (i == 0 && s[i] == '0')//字符串以0开始,要么是0x 0X或者不必用于计算
continue;
else if (s[i] == 'x' || s[i] == 'X')//如果第二位是x 或者 X 那么后两位才是用于16进制转10进制计算所需要的
continue;
else if ('0'<= s[i] && s[i] <= '9')//这里就是字符到对应数大小的转换
n = n * 16 + (s[i] - '0');
else if ('a'<= s[i] && s[i] <= 'f')
n = n * 16 + (s[i] - 'a' + 10);
else if ('A' <= s[i] && s[i] <= 'F')
n = n * 16 + (s[i] - 'A' + 10);
else
return -1;
}
return n;
}
Q:重新编写函数squeeze(s1, s2),将字符串s1中任何与字符串s2中字符匹配的字符都删除。
A:代码如下:
#include <stdio.h>
void squeeze(char s[], char t[]);
void main()
{
char string_1[] = "ggqweewqeqweqweqweqeqweqweqweqhh";
char string_2[] = "qwe";
printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
squeeze(string_1, string_2);
printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
}
void squeeze(char string_1[], char string_2[])
{
int i, j, k;
for (k = 0; string_2[k] != '\0'; k++)
{
for (i = j = 0; string_1[i] != '\0'; i++)
if (string_1[i] != string_2[k])
string_1[j++] = string_1[i];
string_1[j] = '\0';
}
}
Q:重新编写函数any(s1, s2),将字符串s2中任一字符在字符串s1中第一次出现的位置作为结果返回。如果s1中不包含s2中的字符,则返回-1.(标准库函数strpbrk具有同样的功能,但它返回的是指向该位置的指针)
A:代码如下:
#include <stdio.h>
int any(char s1[], char s2[]);
void main()
{
char string_1[] = "ggqweewqeqweqweqweqeqweqweqweqhh";
char string_2[] = "okqwe";
int res;
printf("sting_1 is %s.\nstring_2 is %s.\n", string_1, string_2);
res = any(string_1,string_2);
printf("position is %d.\n", res);
}
int any(char s1[], char s2[])
{
int i, j, posit = -1;
for (i = 0; s2[i] != '\0'; i++)
{
for (j = 0; s1[j] != '\0'; j++)
{
if (s2[i] == s1[j])
{
posit = i;
break;
}
}
if (posit == -1)
continue;
else
break;
}
return posit;
}
Q:编写一个函数setbits(x,p,n,y),该函数返回对x执行下列操作后的结果值:将x从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。
A:代码如下:
#include <stdio.h>
unsigned setbits(unsigned x, int p, int n, unsigned y);
void main()
{
unsigned x = 0xa3, y = 0xb4, res;
//x 163 10100011 y 180 10110100 --> 10101001 163
int p = 1, n = 3;//p从右向左第0位开始
res = setbits(x, p, n, y);
printf("%u.\n", res);
}
unsigned setbits(unsigned x, int p, int n, unsigned y)
{
unsigned temp;
int i;
i = p;
temp = ~0 << n;//取反构造匹配p位开始n个的部分,与之相与使其置零
while(i-- > 0){
temp = temp << 1;
temp++;
}
x = x & temp;
temp = ~(~0 << n);
y = y & temp;//将y移位到对应的位置
while(p-- > 0){
y = y << 1;
y++;
}
return (x | y);//y与之相或,作用是覆盖对应的部分,即替换
}
Q:编写一个函数invert(x,p,n),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位取反(即,1变成0,0变成1),x的其余各位保持不变。
A:代码如下:
#include <stdio.h>
unsigned invert(unsigned x, int p, int n);
void main()
{
unsigned x = 0xa3, y = 0xb4, res;
//x 163 10100011 y 180 10110100 --> 10101001 163
// 10101101 --> 173
int p = 1, n = 3;//p从右向左第0位开始
res = invert(x, p, n);
printf("%u.\n", res);
}
unsigned invert(unsigned x, int p, int n)
{
unsigned temp;
temp = ~0 << n;
while(p-- > 0){
temp = temp << 1;
temp++;
}
return (~(~temp & x) & (~temp | x));
//前半部分得到中间部分为取反结果,两侧是1
//后半部分得到两侧是原结果,中间部分是1
//两者相与两边则是原结果,中间则是取反结果
}
Q:编写一个函数rightrot(x,n),该函数返回将x循环右移(即从最右端移出的位将从最左端移入)n(二进制)位后所得到的值。
A:代码如下:
#include <stdio.h>
unsigned rightrot(unsigned x, int n);
void main()
{
unsigned res, x = 0xa3;
//10100011 --> 01100000000000000000000000010100 电脑上是32位
int n = 3;
res = rightrot(x, n);
printf("%u.\n", res);
}
unsigned rightrot(unsigned x, int n)
{
unsigned temp = ~0;
unsigned temp_1 = ~0;
while(temp != 0)
{
temp_1 = temp_1 << 1;
temp = temp_1 << 1;
}
printf("temp_1 is %u.\n", temp_1);
temp = ~(~0 >> 1);
printf("temp is %u.\n", temp);
while(n-- > 0)
{
temp = (~0 << 1) | x;//得到x最右一位的值 只有两种情况
x = x >> 1;
if (temp == ~0)//x最后一位为1
{
// x = ~(~0 >> 1) | x;
x = temp_1 | x;
}
// else//x最后一位为0
// {
// //x最后一位为0 那么x右移已经自动补0了 这里就不用操作
// }
}
return x;
}
Q:在求对二的补码时,表达式x&=(x - 1)可以删除x中最右边值为1的一个二进制位,请解释这样做的道理。用这一方法重写bitcount函数,以加快其执行速度。
A:x&=(x - 1)等效于 x = x & (x - 1),如果x最右边的一个二进制位为1,那么减1之后前面的位不变,最右这位变成0,这样与原本相“与”,那么x就变成了x-1,且最右一位是0,达到了删除最右边值为1的二进制位效果。更改代码如下(参考的),不过好像效率并没有提高?
#include <stdio.h>
int bitcount(unsigned x);
void main(void)
{
unsigned x = 0xa3;
int b = 0;
b = bitcount(x);
printf("b is %d.\n", b);
}
int bitcount(unsigned x)
{
int b = 0;
while(x != 0)
{
b++;
x &= (x -1);
}
return b;
}
Q:重新编写将大写字母转换为小写字母的函数lower,并用条件表达式替换其中的if-else结构。
A:代码如下:
#include <stdio.h>
int bitcount(unsigned x);
void main(void)
{
char string[] = "WGHigGYljuo";
int n = 0;
while(string[n++] != '\0')
printf("%c", (string[n] >= 'A' && string[n] <= 'Z') ? (string[n] - 'A' + 'a') : string[n]);
printf("\n");
}