指针

指针

       指针是用C语言高效编程的一个基本元素。

       在程序中,计算机使用变量名引用这块内存,但是一旦编译执行程序,计算机就使用内存位置的地址来引用它。

       int *pointer = NULL; //NULL是在标准库中定义的一个常量,对于指针它表示0NULL是一个不指向任何内存位置的值。这表示,使用不指向任何对象的指针,不会意外覆盖内存。

       使用指针时,一定要初始化它们。使用未初始化的指针存储数据项是很危险的。

       long value = 9999L;

       const long *pvalue = &value;

上面的const代表的意思是不能通过pvalue来改变值,但是可以对value进行操作。

       int count = 30

       int *const pcount = &count;

上面的count为常量指针,表示指针中存储的地址不能改变,即pcount指向的总是相同的对象。但仍然可以使用pcount来改变其指向的值。

       int item = 25

       const int *const pitem = &item;

对于上面的语句就表示是一个指向常量的常量指针,所以所有的信息都是固定不变的。既不能改变存储在pitem中的地址,也不能使用pitem改变它指向的内容。

 

       对于一维数组int a[5],可以使用*a来获得第一个值;而对于N维数组就需要用N*来获取第一个变量了。

 

       指针是一个非常灵活且强大的编程工具,有非常广泛的应用。大多数C程序都在某种程度上使用了指针。C语言还进一步增强了指针的功能,为在代码中使用指针提供了很强的激励机制,它允许在执行程序时动态分配内存。只有使用指针,才能动态分配内存

 

       对于malloc函数返回的为void *,表示可以指向任意类型的数据,但是在我们使用的时候,我们需要指定具体是什么类型。

 

       callocmalloc函数相比有两个优点。分别为:

l  它把内存分配为给定大小的数组;

l  它初始化了所分配的内存,所有的位都是0

 

使用动态分配的内存的基本规则:

l  避免分配大量的小内存块,分配堆上的内存有一些系统开销,所以分配许多小的内存块比分配几个大内存块的系统开销大;

l  仅在需要时分配内存,只要使用完堆上的内存块,就释放它;

l  总是确保释放已经分配的内存,在编写分配内存的代码时,就要确定在代码的什么地方释放内存;

l  在释放内存之前,确保不会无意中覆盖堆上分配的内存的地址,否则程序就会出现内存泄露,这个在循环分配内存时,要特别小心。

 

使用指针高效的一个原因:比如比较两个数组的大小,我们只需交换两个数组的指针即可,而数据还位于原来的地方,只是指针变了。

计算器改进程序:

#include <stdio.h>         //standard input/output

#include <string.h> //for string functions

#include <ctype.h> //for classifying characters

#include <stdlib.h> //for converting strings to numeric values

#include <math.h> //for power()function



#define BUFFER_LEN 256



int main(void)

{

char input[BUFFER_LEN]; //input expression

char number_string[30]; //stores a number string from input

char op = 0; //stores an operator

unsigned int index = 0; //index of the current character in input

unsigned int to = 0; //to index for copying input to itself

size_t input_length = 0; //length of the string in input

unsigned int number_length = 0; //length of the string in number_string

double result = 0.0; //the result of an operation

double number = 0.0; //stores the value of number_string



printf("\nTo use this calculator , enter any expression with"

" or without spaces");

printf("\nAn expression may include the operators:");

printf("\n + , - , * , / , %%, or ^(raise to a power).");

printf("\nUse = at the beginning of a line to operate on");

printf("\nThe result of the previous calculatioin.");

printf("\nUse quit by itself to stop the calculator.\n\n");



//the main calculator loop

while (strcmp(fgets(input, BUFFER_LEN, stdin), "quit\n") != 0) {

input_length = strlen(input); //get the input string length

input[--input_length] = '\0'; //remove newline at the end



//remove all spaces from the input by copy the string to itself

//including the string terminating character

for (to = 0, index = 0; index <= input_length; index++)

if (*(input + index) != ' ') //if it is not a space

*(input + to++) = *(input + index); //copy the character

input_length = strlen(input); //get the new string length

index = 0; //start at the first character

//code to implement the calculator

if (input[index] == '=')

index++;

else { //no - look for the left operand

//the first operator

//now look for 'op number' combinations

// for(;index < input_length;)

// {

// op = *(input + index++);//get the operator



//check for sign and copy it

number_length = 0; //initialize length

if (input[index] == '+' || input[index] == '-') //is it + or -

*(number_string + number_length++) = *(input + index++); //yes so copy it



//copy all following digits

for (; isdigit(*(input + index)); index++) //is it a digit

*(number_string + number_length++) =

*(input + index);



//copy any fractional part

if (*(input + index) == '.') //is it decimal point

{

//yes so copy the decimal point and the following digits

*(number_string + number_length++) =

*(input + index++);



for (; isdigit(*(input + index)); index++) //for each digit

*(number_string + number_length++) = *(input + index); //copy it

}

*(number_string + number_length) = '\0'; //append string terminator



//if we have a left operand, the length of number_string

//will be > 0. In this case convert to a double so we

//can use it in the calculation

if (number_length > 0)

result = atof(number_string); //store first number as result



}



//now look for 'op number' combinations

for (; index < input_length;) {

op = *(input + index++); //get the operator



//check for sign and copy it

number_length = 0; //initialize length

if (input[index] == '+' || input[index] == '-') //is it + or -

*(number_string + number_length++) = *(input + index++); //yes so copy it



//copy all following digits

for (; isdigit(*(input + index)); index++) //is it a digit

*(number_string + number_length++) =

*(input + index);



//copy any fractional part

if (*(input + index) == '.') //is it decimal point

{

//yes so copy the decimal point and the following digits

*(number_string + number_length++) =

*(input + index++);



for (; isdigit(*(input + index)); index++) //for each digit

*(number_string + number_length++) = *(input + index); //copy it

}

*(number_string + number_length) = '\0'; //append string terminator



//if we have a left operand, the length of number_string

//will be > 0. In this case convert to a double so we

//can use it in the calculation

number = atof(number_string); //store first number as result



//execute operatioin , as 'result op= number'

switch (op) {

case '+':

result += number;

break;

case '-':

result -= number;

break;

case '*':

result *= number;

break;

case '/':

//check second operand for zero

if (number == 0)

printf

("\n\n\aDivision by zero error!\n");

else

result /= number;

break;

case '%':

//check second operand for zero

if ((long) number == 0)

printf

("\n\n\aDivision by zero error!\n");

else

result =

(double) ((long) result %

(long) number);

break;

case '^':

result = pow(result, number);

break;

default:

printf("\n\n\aIllegal operation!\n");

break;

}

}

//code to analyze the operator and right operand

//and produce the result

printf("= %f\n", result);

}

return 0;

}

字符串和文本的应用

字符串和文本的应用

       C语言没有提供字符串数据类型,C语言使用char类型的数组元素存储字符串。

       字符串都以\0结尾,比如:

printf(“The character \0 is used to terminate a string.”);

       将只输出The character

 

       char string[N];只能存储一个至N-1个字符的字符串,因为必须给终止字符\0提供一个数组元素,当然也可以使用这个数组存储20个字符,但那就不是一个字符串了。

 

       使用char数组存储各种不同的字符串的主要缺点是浪费内存。因为根据定义,数组的长度是固定的,必须用足以容纳要存储的最大字符串长度来生命数组的大小。在大多数情况下,一般的字符串都会小于这个最大值,所以总是会浪费内存。

 

       如果需要合并两个字符串,一定要确定第一个字符串的可用长度要大于两个字符串的实际长度才可以哟~

 

       对于N维的数组,第一维可以不指定值,后面的几维必须指定。

 

       关于size_t类型,主要是为了移植性,代表的是unsigned int。在各种c语言中,sizeofstrlen函数的返回类型互不相同,这是又编译器的作者来决定的,把这个类型定义为size_t,并把size_t定义放在头文件中,非常便于在代码中包容这种依赖性。

 

       指针:含有地址的变量,它含有内存中另一个包含数值的位置的引用。

 

gets()和fgets()函数的区别在于,按下回车键时,gets()读取的输入会附加上一个空字符\0,而fgets()读取的输入会附加上\n\0。所以,使用fgets()的时候要特别注意,将最后添加上的\n区别对待。

 

       宽字符串常量只需要在其前面加上L修饰符即可:比如:

wchar_t proverb[] = L”A nod is as good as a wink to a blind horse”;记得如果要将宽字符输出printf,要使用%S格式限制符,而不能使用%s,免得printf函数假定字符串包含单字节字符,这样的话结果就不正确了。

 

下面是一个统计字符的程序,很不错:

#include <stdio.h>

#include <string.h>

#include <stdlib.h>



#define TEXTLEN 10000

#define BUFFERSIZE 100

#define MAXWORDS 500

#define WORDLEN 15

const char space = ' ';

const char quote = '\'';

#define true 1

#define false 0



int main(void)

{

char text[TEXTLEN + 1];

char buffer[BUFFERSIZE];

char endstr[] = "*\n";



char words[MAXWORDS][WORDLEN + 1];

int nword[MAXWORDS];

char word[WORDLEN + 1];

int wordlen = 0;

int wordcount = 0;





printf("Enter text on an arbitrary number of lines.");

printf("\nEnter a line containing just an asterisk(*) to end input:\n\n");



//read an arbitrary number of lines of text

//while(true)

while(1)

{

//a string containing an asterisk followed by newline

//signals end of input



if(!strcmp(fgets(buffer,BUFFERSIZE,stdin),endstr))

break;



//check if we have space for latest input

if(strlen(text) + strlen(buffer) +1 > TEXTLEN)

{

printf("Maximun capacity for text exceeded. Terminating program.");

return 1;

}

strcat(text,buffer);

}



//plus the rest of the program code....



//replace everything except alpha and single quote characters by spaces

int i;

for(i = 0; i < strlen(text);i++)

{

if(text[i] == quote || isalnum(text[i]))

continue;

text[i] = space;

}





//find unique words and store in words array

int index = 0;

while(1)

{

//ignore any leading space before a word

while(text[index] == space)

++index;



//if we are at the end of the text, we are done

if(text[index] == '\0')

break;



//extract a word

wordlen = 0;

while(text[index] == quote || isalpha(text[index]))

{

//check if word is too long

if(wordlen == WORDLEN)

{

printf("Maximun word length exceeded. Terminating program.");

return 1;

}

word[wordlen++] = tolower(text[index++]);

}

word[wordlen] = '\0';



//check for word already stored

int isnew = true;

for(i = 0; i < wordcount;i++)

if(strcmp(word,words[i]) == 0)

{

++nword[i];

isnew = false;

break;

}



if(isnew)

{

//check if we have space for another word

if(wordcount >= MAXWORDS)

{

printf("\n Maximun word count exceeded. Terminating program.'");

return 1;

}

strcpy(words[wordcount],word);

nword[wordcount++] = 1;

}

}

//output the words and frequencies

for(i =0;i<wordcount;i++)

{

if(!(i%3))

printf("\n");

printf("%-15s%5d ",words[i],nword[i]);

}

printf("\n");

return 0;

}

函数再探

函数再探

       指针对于操作数据和含有数据的变量时一个非常有用的工具。只要有一个指针就可以处理所有的数据。同样,使用指针也可以操作函数,函数的内存地址存储了函数开始执行的位置,存储在函数指针中的内容就是这个地址。

声明函数指针

       函数指针的声明看起来有点奇怪,容易混淆,比如,模式为:

int (*pfunction) (int);

       注意pfunction*的括号不能去掉,不然就不是一样的含义了,就编程了pfunction函数的声明了。

通过函数指针调用函数

       比如有如下函数原型:

int sum(int a, int b);

可以通过int (*pfun)(int,int)=sum;来将sum的地址存储在指针pfun中,然后我们就可以使用pfun了,比如:

int result = pfun(45,55);

       所以,我们看到其实函数的用法非常类似于数组,如果需要的是数组的地址,只要使用数组名即可,同样,如果需要的是函数的地址,也是只使用函数名即可。

函数指针数组

       函数指针和一般的变量是一样的,所以可创建函数指针的数组。要声明函数之后在呢数组,只需将数组的大小放在函数指针数组名之后即可,例如:

       int (*pfunctions[10]) (int);

使用:

int sum(int a, int b);

int (*pfunctions[10]) (int,int);

pfunctions[0] = sum;

就可以使用pfunctions[0](xy);来计算xy的和。

也可以这样初始化指针数组的所有元素:

int (*pfunctions[3]) (int,int) = {sum,product,difference};

作为变元的函数指针

       函数指针也可以作为变元来传递,这样就可以根据指针所指向的函数,而调用不同的函数了。比如:

       int any_function(int (*pfun)(int,int),int x,int y);

 

int any_function(int (*pfun)(int,int),int x,int y)

{

       return pfun(x,y);

}

 

       将程序分解为函数,不仅简化了开发程序的过程,还增强了程序语言解决问题的能力。设计优良的函数常常可以重用,使新应用程序的开发变得更快、更简单。标准库就证明了可重用函数的威力。

静态常量:函数内部的追踪

       C语言中的一个关键字static声明的为静态常量,特点为:

l  虽然static在函数的作用域内定义,但是当执行退出该函数后,这个静态变量不会删除;

l  自动变量每次进入作用域时,都会初始化一次,但是声明为static的变量只在程序开始时初始化一次;

l  静态变量只能在包含其声明的函数中可见,但是它是一个全局变量,因此可以用全局变量的方式使用它;

l  只要程序开始执行,静态变量就一直存在,但是它只能在声明它的范围内可见,不能再该作用域的外部引用。

 

对于全局变量,虽然很有好处,比如可以简化并缩短某些程序,但是过度使用会使程序容易出错,主要原因是很容易修改全局变量,而忘记它对整个程序带来的后果。所以,最好不要给本地变量和全局变量使用相同的名字,这不但没有好处,反而有坏处。

变元个数可变的函数

       标准库stdarg.h提供了编写这种函数的例程。

比如,一个求均值的函数:

double average(double v1,double v2,…)

{

       va_list parg;

       double sum = v1 + v2;

       double value = 0;

       int count = 2;

 

       va_start(parg,v2);

       while((value = va_arg(parg,double)) != 0.0 )//这里要求最后一个值为0

{

              sum += value;

              count ++;

}

       va_end(parg);

       return sum/count;

}

长度可变的变元列表的基本规则

l  在变元数目可变的函数中,至少要有一个固定变元;

l  必须调用va_start初始化函数中可变变元列表指针的值,变元指针的类型必须声明为va_list类型;

l  必须有确定每个变元类型的机制;

l  必须有确定何时终止变元列表的方法;

l  va_arg的第二个变元指定了变元值的类型,这个指针类型可以在类型名的后面加上*来指定;

l  在退出变元数目可变的函数前,必须调用va_end,否则,函数将不能正常工作。

结束程序

结束程序由三种方式:

l  return

l  abort  这种表示是非正常结束;

l  exit –通常0表示正常结束,其他值以某种方式代表程序的状态。

提高性能

       有两个工具可以使编译器生成性能更高的代码。其中一个与短函数调用的编译方式相关,另一个涉及指针的使用。

内联声明函数

       C语言的功能结构要求将程序分解为许多函数,函数有时可以非常短,短函数的每次调用可以用实现该函数功能的内联代码替代,提高执行性能。要采用这种技术,可以使用内联指定短函数。

       inline double sumdouble x, double y

{

       return x+y;

}

 

一、inline 关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义。

表达式形式的宏定义一例:

#define ExpressionName(Var1,Var2) ((Var1)+(Var2))*((Var1)-(Var2))为什么要取代这种形式呢,且听我道来:

1.        首先谈一下在C中使用这种形式宏定义的原因,C语言是一个效率很高的语言,这种宏定义在形式及使用上像一个函数,但它使用预处理器实现,没有了参数压栈,代码生成等一系列的操作,因此,效率很高,这是它在C中被使用的一个主要原因。

2.        这种宏定义在形式上类似于一个函数,但在使用它时,仅仅只是做预处理器符号表中的简单替换,因此它不能进行参数有效性的检测,也就不能享受C++编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。

3.        C++中引入了类及类的访问控制,这样,如果一个操作或者说一个表达式涉及到类的保护成员或私有成员,你就不可能使用这种宏定义来实现(因为无法将this指针放在合适的位置)

4.        inline 推出的目的,也正是为了取代这种表达式形式的宏定义,它消除了它的缺点,同时又很好地继承了它的优点。

为什么inline能很好地取代预定义呢?

对应于上面的1-3点,阐述如下:

1.        inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开),没有了调用的开销,效率也很高。

2.        很明显,类的内联函数也是一个真正的函数,编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。然后进行一系列的相关检查,就像对待任何一个真正的函数一样。这样就消除了它的隐患和局限性。

3.        inline 可以作为某个类的成员函数,当然就可以在其中使用所在类的保护成员及私有成员。

在何时使用inline函数:

首先,你可以使用inline函数完全取代表达式形式的宏定义。

另外要注意,内联函数一般只会用在函数内容非常简单的时候,这是因为,内联函数的代码会在任何调用它的地方展开,如果函数太复杂,代码膨胀带来的恶果很可能会大于效率的提高带来的益处。内联函数最重要的使用地方是用于类的存取函数。

使用restrict关键字

       为了优化设计指针的代码,编译器必须能肯定指针没有别名的换言之,每个指针引用的数据项都没有在给定范围内以其他方式引用。关键字restrict就可以告诉编译器,合适出现这种情况,并允许应用代码优化功能。

       在大多数情况下,不需要使用关键字restrict,只有代码进行大量计算,进行代码优化才有显著的效果,而这还取决于编译器。

游戏

//Program reversi an otherlo type game

#include <stdio.h>

#include <stdbool.h>

#include <ctype.h>

#include <string.h>



//const int SIZE = 6; //board size, must be even

#define SIZE 6

const char comp_c = '@'; //computer's counter

const char player_c = 'O'; //player's counter



//functional prototypes

void display(char board[][SIZE]);

int valid_moves(char board[][SIZE],bool moves[][SIZE],char player);

void make_move(char board[][SIZE],int row,int col, char player);

void computer_move(char board[][SIZE],bool moves[][SIZE],char player);

int best_move(char board[][SIZE],bool moves[][SIZE],char player);

int get_score(char board[][SIZE],char player);



int main(void)

{

char board[SIZE][SIZE] = { 0 }; //the board

bool moves[SIZE][SIZE] = {false}; //valid moves

int row = 0; //board row index

int col = 0; //board column index

int no_of_games =0;//number of games

int no_of_moves = 0;//count of moves

int invalid_moves = 0;//invalid move count

int comp_score = 0;//computer score

int user_score = 0;//player score

char y = 0;//column letter

int x = 0;//row number

char again = 0;//replay choice input



//player indicator : true for player and false for computer

bool next_player = true;



printf("\nREVERSI\n\n");

printf("You can go first on the first game, then we will take turns.\n");

printf(" You will be while - (%c)\n I will be black - (%c).\n",

player_c,comp_c);

printf("Select a square for your move by typing a digit for the row\n "

"and a letter for the column with no spaces between.\n");

printf("\nGood luck ! Press Enter to start.\n");

scanf("%c",&again);



//the main game loop

do

{//on even games the player starts;

//on odd games the computer starts;

next_player = !next_player;

no_of_moves = 4;//starts with four counters



//blank all the board squares

for(row = 0; row < SIZE; row ++)

for (col = 0 ; col < SIZE; col ++)

board[row][col] = ' ';



//place the initial four counters in the center

int mid = SIZE/2;

board[mid - 1][mid - 1] = board[mid][mid] = player_c;

board[mid - 1][mid] = board[mid][mid - 1] = comp_c;



//the game play loop

do

{

display(board); //display the board

if(next_player = !next_player)

{

//it is the player's turn

//code to get the player's move and execute it

if(valid_moves(board,moves,player_c))

{

//read player moves until a valid move is entered

for(;;)

{

printf("Please enter your move (row column): ");

scanf("%d%c",&x,&y);//read input

y = tolower(y) - 'a';//convert to column index

x --;//convert to row index

if(x >= 0 && y >= 0 && x < SIZE && y < SIZE && moves[x][y])

{

make_move(board,x,y,player_c);

no_of_moves++;//increment move count

break;

}

else

printf("Not a valid move , try again\n");

}

}

else//no valid moves

if(++invalid_moves < 2)

{

printf("\nYou have to pass, press return");

scanf("%c",&again);

}

else

printf("\nNeither of us can go, so the game is oves. \n");

}

else

{

//it is the computer's turn

//code to make the computer's move

if(valid_moves(board,moves,'@'))//check for valid moves

{

invalid_moves = 0;//reset invalid count

computer_move(board,moves,'@');

no_of_moves++;//increment move count

}

else

{

if(++invalid_moves < 2)

printf("\nI have to pass, your go\n");//no valid move

else

printf("\nNeither of us can go, so the game is over.\n");

}

}

}while(no_of_moves < SIZE*SIZE && invalid_moves < 2);



//game is over

display(board); //show final board



//get final scores and display them

comp_score = user_score = 0;

for(row = 0; row < SIZE; row ++)

for(col = 0 ;col < SIZE; col++)

{

comp_score += board[row][col] == comp_c;

user_score += board[row][col] == player_c;

}

printf("The final score is: \n");

printf("Computer %d\nUser %d\n",comp_score,user_score);



printf("Do you want to play again (y/n):");

scanf("%c",&again);//get y or n

}while(tolower(again) == 'y');

printf("\nGoodby\n");

return 0;

}



//Function to display the board in its

//current state with row numbers and column

//letters to identify squares

//parameter is the board array

void display(char board[][SIZE])

{

//display the column labels

char col_label = 'a';//column label

printf("\n "); //start top line

int col;

for(col = 0; col < SIZE; col++)

printf(" %c ",col_label+col);///display the top line

printf("\n");//end the top line



//display the rows...

int row;

for (row = 0; row < SIZE; row ++)

{

//display the top line for the current row

printf(" +");



for(col = 0; col < SIZE; col++)

printf("---+");

printf("\n%2d|",row + 1);



//display the counters in current row

for(col = 0 ; col < SIZE; col ++)

printf(" %c|",board[row][col]);//display counters in row

printf("\n");

}

//finally display the bottom line of the board

printf(" +");//start the bottom line

for(col = 0; col < SIZE; col ++)

printf("---+");

printf("\n");



}



//calculates which squares are valid moves

//for player, Valid moves are recorded in the

//moves array - true indicates a valid move ,

//false indicates an invalide move

//first parameter is the board array

//second parameter is the moves array

//third parameters identifies the palyer

//to make the move

//Return valid move count



int valid_moves(char board[][SIZE],bool moves[][SIZE],char player)

{

int rowdelta = 0 ;//row increment aroud a square

int coldelta = 0;//column increment around a square

int x = 0;//row index when searching

int y = 0;//column index when searching

int no_of_moves = 0;//number of valid moves

int row,col;

//set the oppoonent

char opponent = (player == player_c) ? comp_c : player_c;



//initialize moves array to false

for(row = 0; row < SIZE; row ++)

for(col = 0; col < SIZE;col++)

moves[row][col] = false;





//find squares for valid moves

//a valid mvoe must be on a blank square and must enclose

//at least one opponent between two player squares

for(row = 0;row < SIZE;row++)

for(col = 0; col < SIZE; col++)

{

if(board[row][col] != ' ')//is it a blank sqare?

continue;



//check all the sqares aroud the blank square

//for the opponents counter

for(rowdelta = -1; rowdelta <= 1; rowdelta++)

for(coldelta = -1; coldelta <= 1; coldelta++)

{

//do not check outside the array or the current square

if(row + rowdelta < 0 || row + rowdelta >= SIZE ||

col + coldelta <0 || col + coldelta >= SIZE ||

(rowdelta == 0 && coldelta == 0))

continue;

//now check the square

if(board[row + rowdelta][col + coldelta] == opponent)

{

//if we find the opponent , move in the delta direction

//over opponent counters searching for a player counter

x = row + rowdelta;//move to

y = col + coldelta;//opponet square



//look for a player sqare in the delta directioin

for(;;)

{

x += rowdelta;//go to next square

y += coldelta;//in delta direction



//if we move outside the array, give up

if(x < 0 || x >= SIZE || y < 0 || y >=SIZE)

break;



//if we find a blank squaer , give up

if(board[x][y] == ' ')

break;





//if the square has a player counter

//then we have a valid move

if(board[x][y] == player)

{

moves[row][col] = true;//mark as valid

no_of_moves++;//increase valid moves count

break;//go check another sqares

}

}

}

}

}

return no_of_moves;

}



//make a move , this places the counter on a sqare and reverses

//all the oppnent's counters affected by the move.

//first parameter is the board array

//second and third parameters are the row and column indices.

//fourth paramter identified the player



void make_move(char board[][SIZE],int row,int col, char player)

{

int rowdelta = 0;//row increment

int coldelta = 0;//column increment

int x = 0;//row index for searching

int y = 0;//column index for searching



//identify opponent

char opponent = (player == player_c) ? comp_c : player_c;

board[row][col]=player;//place the palyer counter



//check all the squares aroud this square

//for the opponents counter

for(rowdelta = -1; rowdelta <= 1; rowdelta++)

for(coldelta = -1; coldelta <= 1; coldelta++)

{

//do no check off the board, or the current square

if(row + rowdelta < 0 || row + rowdelta >= SIZE ||

col + coldelta <0 || col + coldelta >= SIZE ||

(rowdelta == 0 && coldelta == 0))

continue;



//now check the square

if(board[row+rowdelta][col+coldelta] == opponent)

{

//if we find the opponet , search in the same direction

//for a player counter

x = row + rowdelta;//move to opponent

y = col + coldelta;//square

}



for(;;)

{

x += rowdelta;//move to the next square

y += coldelta;



//if we are off the board give up

if(x < 0 || x >= SIZE || y < 0 || y >= SIZE)

break;

//if the square is blank give up

if(board[x][y] == ' ')

break;



//if we find the player counter, go backward from here

//changing all the opponents counters to player

if(board[x][y] == player)

{

while(board[x -= rowdelta][y -= coldelta] == opponent)

//opponent

board[x][y] = player;//yes change it ,

break; //we are done

}

}

}

}



//find the best move for the computer, this is the move for

//which the opponent's best possible move score is a minimu

//first parameter is teh board array

//second parameter is the moves array containing valid moves

//third parameter identified the computer

void computer_move(char board[][SIZE],bool moves[][SIZE],char player)

{

int best_row = 0;//best row index

int best_col = 0;//best column index

int new_score = 0;//score for current move

int score = 100;//minimum opponent score

char temp_board[SIZE][SIZE];//local copy of board

bool temp_moves[SIZE][SIZE];//local valid moves array

int row,col;

//identify opponent

char opponent = (player == player_c) ? comp_c : player_c;



//go through all valid moves

for(row = 0; row < SIZE; row ++)

for(col = 0; col < SIZE; col ++)

{

if(!moves[row][col])

continue;



//first make copies of the board array

memcpy(temp_board,board,sizeof(temp_board));



//now make this move on the temporary board

make_move(temp_board,row,col,player);



//find valid moves for the opponent after this move

valid_moves(temp_board,temp_moves,opponent);



//now find the score for the opponent's best move

new_score = best_move(temp_board,temp_moves,opponent);



if(new_score < score)//is it worse

{//yes, so save this move

score = new_score;//record new lowest opponent score

best_row = row;//record best move row

best_col = col;//and column

}

}

make_move(board,best_row,best_col,player);



}



//calculates the score for the current board position for the

//player, player counters score +1, opponent counters score -1

//first parameter is the board array

//second parameter identified the player

//return value is the score

int get_score(char board[][SIZE],char player)

{

int score = 0; //score for current position

int row,col;

//identify opponent

char opponent = (player == player_c) ? comp_c : player_c;



//check all board squares

for(row = 0 ; row < SIZE; row ++)

for(col = 0; col < SIZE; col++)

{

score -= board[row][col] == opponent;//decrement for opponent

score += board[row][col] == player;//increment for player

}

return score;

}



//calculates the score for the best move out of the valid moves

//for player in the current position

//first parameter is the board array

//second parameter is the moves array defining valid moves

//third parameter identifies the player

//the score for the best move is returned

int best_move(char board[][SIZE],bool moves[][SIZE],char player)

{

//identify opponent

char opponent = (player == player_c) ? comp_c : player_c;

int row,col;

char new_board[SIZE][SIZE] = {0};//local copy of board

int score = 0;//best score

int new_score = 0;//score for current move



//check all valid moves to find the best

for(row = 0; row < SIZE; row++)

for(col = 0; col < SIZE; col++)

{

if(!moves[row][col])

continue;//not a valid move? go to the next



//copy the board

memcpy(new_board,board,sizeof(new_board));



//make move on the board copy

make_move(new_board,row,col,player);



//get score for move

new_score = get_score(new_board,player);



if(score < new_score)//is it better,

score = new_score;//yes save it as best score

}

return score;//return best score

}

编程初步

编程初步

遵从C语言标准的编译器至少支持31个字符,只要不超过这个长度就没有问题。

       打印printf需要多行时,每行都需要有自己的一对双引号。

       当一个操作数是负数时,使用除法和模数运算符的结果为:

       表达式-45/745/-7的除法结果相同都为-6

       但是模数运算符-45/745/-7的结果分别为-33,因为模数运算符的结果总是和左操作数的符号相同

       在处理不能为负的值时,需要使用无符号的unsigned类型,比如人的年龄。

       整型int变量类型的大小是最适合执行代码的计算机类型,因为int类型对应计算机上整数的大小,使得计算机可以发挥其最大效能。

       short int类型可以简写为short,就像long int 也可以简写为long一样。

       打印print的时候,需要的参数可以另起一行,这样方便理解。

       注意:100int类型,而100Llong类型,long long类型为100LL;如果为unsigned long应写为100UL

       由于浮点数的表示方式,虽然它的位数是固定的,但是它的取值范围要比整数大很多

       浮点数0.5E-15表示为0.0000000000000005

       注意:小数的精确位数只是一个大约的数,因为浮点数在内部是以2进制方式存储的,十进制的浮点数在二进制中并不总是有精确的表示形式。

       编写一个类型为float的常量,需要在数值的末尾添加一个f,以区别double类型。而long double类型的变量就需要在数字的末尾加上一个大写的L或小写的l

       输出的字段宽度是输出值所使用的总字符数,%[+/-]width.precision,其中width比较利于对齐,而precision指定精度。在指定字段宽度时,数值默认为右对齐,如果希望数值左对齐,可以在%后面添加一个符号。

定义常量

       如果一个值是不会改变的,这里有两种方法来定义它:

1.      使用#define PI 3.1415926f

2.      使用const float Pi=3.1415926f//const固化变量的值,但注意,变量值必须出现在声明语句中;

 

隐式类型转换:在二元算术运算中使用不同类型的操作数,编译器就会把其中一个值域较小的操作数类型转换为另一个操作数的类型。

       无符号整数类型的级别从低到高为:signed charshortintlonglong long

 

       char类型的变量有双重性,可以把它解释为一个字符,也可以解释为一个整数。

 

l  ptrdiff_t 带符号的整数类型, 用来表示指针相减的结果类型

l  wchar_t 宽字符类型:存储多字节字符码,一般占两个字节;(在char类型的字符常量前面加上修饰符L就可以定义一个宽字符常量,比如wchar_t w_ch = L’A’;),要从键盘上把一个字符读入wchar_t类型的变量,可以使用%lc格式指定符;

l  size_t 无符号整数类型, 用来表示sizeof操作符的结果类型

枚举enum

枚举在C/C++中,是一个被命名的整型常数的集合, 枚举在日常生活中很常见。常常希望变量存储一组可能值中的一个。

例如表示星期的SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,SATURDAY, 就是一个枚举。

枚举的说明与结构和联合相似, 其形式为:

enum 枚举名{

标识符[=整型常数],

标识符[=整型常数],

标识符[=整型常数]

} 枚举变量;

如果枚举没有初始化, 即省掉"=整型常数", 则从第一个标识符开始, 顺次赋给标识符0, 1, 2, …。但当枚举中的某个成员赋值后, 其后的成员按依次加1的规则确定其值。

可以给任意或所有枚举器明确指定自己的整数值,尽管枚举器使用的名称必须唯一,但枚举器的值不要求是唯一的。除非有特殊的原因让某些枚举器的值相同,否则一般应确保这些值也是唯一的。

在输出枚举类型的变量时,会得到数值。如果要输出枚举器的名称,必须提供相应的程序逻辑。

例如下列枚举说明后, x1, x2, x3, x4的值分别为0, 1, 2, 3

enum Num{x1, x2, x3, x4}x;

当定义改变成:

enum Num

{

x1,

x2=0,

x3=50,

x4

}x;

x1=0, x2=0, x3=50, x4=51

注意:

l  枚举中每个成员(标识符)结束符是",", 不是";", 最后一个成员可省略","

l  初始化时可以赋负数, 以后的标识符仍依次加1

l  枚举变量只能取枚举说明结构中的某个标识符常量。

例如:

enum Num

{

x1=5,

x2,

x3,

x4

};

enum Num x=x3;

此时, 枚举变量x实际上是7

枚举类型变量的赋值和使用

枚举类型在使用中有以下规定:

l  枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。例如对枚举weekday的元素再作以下赋值: sun=5;mon=2;sun=mon; 都是错误的。

l  枚举元素本身由系统定义了一个表示序号的数值,从0 开始顺序定义为012…。如在weekday中,sun值为0mon值为1 …,sat值为6

main(){

enum weekday

{ sun,mon,tue,wed,thu,fri,sat } a,b,c;

a=sun;

b=mon;

c=tue;

printf("%d,%d,%d",a,b,c);

}

l  只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如: a=sun;b=mon; 是正确的。而: a=0;b=1; 是错误的。如一定要把数值赋予枚举变量,则必须用强制类型转换,如: a=(enum weekday)2;其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于:a=tue; 还应该说明的是枚举元素不是字符常量也不是字符串常量, 使用时不要加单、双引号

 

可以给枚举类型指定一组可能的值,但是没有检查机制来确保程序只使用这些值。所以程序员要确保只为给定的枚举类型使用有效的枚举值。

       在创建枚举类型的变量时,可以不指定标记,这样就没有枚举类型名了。未命名枚举类型的主要限制是,必须在定义该类型的语句中声明它的所有变量;由于没有类型名,因此无法在代码的后面定义该类型的其他变量。

 

       目前布尔值可以使用bool定义,在未引入bool之前,使用_Bool来定义。对于较早的程序,可以通过来<stdbool.h>来使用bool类型。

 

       复数的头文件为<complex.h>

       有三种类型的复数分别为float complexdouble complexlong double complex。比如float complex z1,那么就是定义z1的实部和虚部都是float类型的,比如z1=a+b*I,其中I就是虚部的i-1的平方)。

l  可以使用creal函数和cimag函数返回double complex类型的实部和虚部;

l  可以使用crealf函数和cimagf函数返回float complex类型的实部和虚部;

l  可以使用creall函数和cimagl函数返回long double complex类型的实部和虚部;

l  conj()函数返回double参数的轭。

 

这里需要注意的是%+d指定的格式总会输出符号,而如果省略了+,那么只有在值为负的时候才会输出,那对于2+3i复数的+号就无法输出,所以我们最好在处理复数的时候加上+格式控制符。

数学函数math.h所包含的函数

数学函数库,一些数学计算的公式的具体实现是放在math.h里,具体有:

1 三角函数

double sin (double);正弦

double cos (double);余弦

double tan (double);正切

2 、反三角函数

double asin (double); 结果介于[-PI/2,PI/2]

double acos (double); 结果介于[0,PI]

double atan (double); 反正切(主值),结果介于[-PI/2,PI/2]

double atan2 (double,double); 反正切(整圆值),结果介于[-PI,PI]

3 、双曲三角函数

double sinh (double);

double cosh (double);

double tanh (double);

4 、指数与对数

double frexp(double value,int *exp);这是一个将value值拆分成小数部分f和(以2为底的)指数部分exp,并返回小数部分f,即f*2^exp。其中f取值在0.5~1.0范围或者0

double ldexp(double x,int exp);这个函数刚好跟上面那个frexp函数功能相反,它的返回值是x*2^exp

double modf(double value,double *iptr);拆分value值,返回它的小数部分,iptr指向整数部分。

double log (double); e为底的对数

double log10 (double);以10为底的对数

double pow(double x,double y);计算以x为底数的y次幂

float powf(float x,float y); 功能与pow一致,只是输入与输出皆为浮点数

double exp (double);求取自然数e的幂

double sqrt (double);开平方

5 、取整

double ceil (double); 取上整,返回比x大的最小整数

double floor (double); 取下整,返回比x小的最大整数,即高斯函数 [x]

6 、绝对值

int abs(int i); 求整型的绝对值

double fabs (double);求实型的绝对值

double cabs(struct complex znum) ;求复数的绝对值

7 、标准化浮点数

double frexp (double f,int *p); 标准化浮点数,f = x * 2^p,已知fx,p (x介于[0.5,1])

double ldexp (double x,int p); frexp相反,已知x,pf

8 、取整与取余

double modf (double,double*); 将参数的整数部分通过指针回传,返回小数部分

double fmod (double,double); 返回两参数相除的余数

9 、其他

double hypot(double x,double y);已知直角三角形两个直角边长度,求斜边长度

double ldexp(double x,int exponent);计算x*2exponent次幂)

double poly(double x,int degree,double coeffs []);计算多项式

int matherr(struct exception *e);数学错误计算处理程序