📄 ct4
字号:
.NHPointers.PPA.ulpointerin C is the address of something.It is a rare case indeed when we care whatthe specific address itself is,but pointers are a quite common way to get atthe contents of something.The unary operator `&' is used to produce the address ofan object, if it has one. Thus.E1 int a, b; b = &a;.E2puts the address of.UL ainto.UL b\*.We can't do much with it except print it or pass itto some other routine, because we haven't given.UL bthe right kind of declaration.But if we declare that.UL bis indeed a.ulpointerto an integer, we're in good shape:.E1 int a, \**b, c; b = &a; c = \**b;.E2.UL bcontains the address of.UL aand.UL `c .UL =.UL \**b'means to use the value in.UL bas an address, i.e., as a pointer.The effect is that we get back the contents of .UL a,albeit rather indirectly.(It's always the case that .UL `\**&x'is the same as.UL xif.UL xhas an address.).PPThe most frequent use of pointers in C is for walkingefficiently along arrays.In fact,in the implementation of an array,the array namerepresents the address of the zeroth element of the array,so you can't use it on the left side of an expression.(You can't change the address of something by assigning to it.)If we say.E1char \**y;char x[100];.E2.UL yis of type pointer to character(although it doesn't yet point anywhere).We can make .UL ypoint toan element of.UL xby either of.E1y = &x[0];y = x;.E2Since .UL xis the address of.UL x[0]this is legal and consistent..PPNow .UL `\**y'gives.UL x[0]\*.More importantly,.E1\**(y+1) gives x[1]\**(y+i) gives x[i].E2and the sequence.E1 y = &x[0]; y\*+;.E2leaves .UL ypointing at.UL x[1]\*..PPLet's use pointersin a function.UL lengththat computes how long a character array is.Remember that by convention all character arrays areterminated with a `\\0'.(And if they aren't, this program will blow up inevitably.)The old way:.E1length(s) char s[ ]; { int n; for( n=0; s[n] != '\\0'; ) n\*+; return(n);}.E2Rewriting with pointers gives.E1length(s) char \**s; { int n; for( n=0; \**s != '\\0'; s\*+ ) n\*+; return(n);}.E2You can now see why we have to say what kind of thing.UL spoints to _if we're to increment it with.UL s\*+we have to increment it by the right amount..PPThe pointer version is more efficient(this is almost always true)but even more compact is.E1 for( n=0; \**s\*+ != '\\0'; n\*+ );.E2The .UL `\**s'returns a character;the.UL `\*+'increments the pointer so we'll get the next characternext time around.As you can see, as we make things more efficient,we also make them less clear.But.UL `\**s\*+'is an idiom so commonthat you have to know it..PPGoing a step further,here's our function.UL strcopythat copies a character array.UL sto another.UL t\*..E1strcopy(s,t) char \**s, \**t; { while(\**t\*+ = \**s\*+);}.E2We have omitted the test against `\\0',because `\\0' is identically zero;you will often see the code this way.(You .ulmusthave a space after the `=':see section 25.).PPFor argumentsto a function, and there only,the declarations.E1char s[ ];char \**s;.E2are equivalent _ a pointer to a type,or an array of unspecified size of that type, are the same thing..PPIf this all seems mysterious, copy these forms until they become second nature.You don't often need anything more complicated..NHFunction Arguments.PPLook back at the function.UL strcopyin the previous section.We passed it two string names as arguments, then proceededto clobber both of them by incrementation.So how come we don't lose the original stringsin the function that called.UL strcopy?.PPAs we said before,C is a ``call by value'' language:when you make a function call like.UL f(x),the.ulvalueof.UL xis passed,not its address.So there's no way to .ulalter.UL xfrom inside.UL f\*.If.UL xis an array.UL (char .UL x[10])this isn't a problem, because.UL x.ulisan address anyway,and you're not trying to change it,just what it addresses.This is why.UL strcopyworks as it does.And it's convenient not to have to worry aboutmaking temporary copies of the input arguments..PPBut what if.UL xis a scalar andyou do want to change it?In that case,you have to pass the.uladdressof.UL xto.UL f,and then use it as a pointer.Thus for example, to interchange two integers, we must write.E1flip(x, y) int \**x, \**y; { int temp; temp = \**x; \**x = \**y; \**y = temp;}.E2and to call .UL flip,we have to pass the addresses of the variables:.E1flip (&a, &b);.E2.NHMultiple Levels of Pointers; Program Arguments.PPWhen a C program is called,the arguments on the command line are made availableto the main program as an argument count.UL argcand an array of character strings.UL argvcontaining the arguments.Manipulating these arguments is one of the most common uses ofmultiple levels of pointers(``pointer to pointer to ...'').By convention,.UL argcis greater than zero;the first argument(in.UL argv[0])is the command name itself..PPHere is a program that simply echoes its arguments..E1main(argc, argv) int argc; char \**\**argv; { int i; for( i=1; i < argc; i\*+ ) printf("%s ", argv[i]); putchar('\\n');}.E2Step by step:.UL mainis called with two arguments,the argument count and the array of arguments..UL argvis a pointer to an array,whose individual elements are pointers to arrays of characters.The zeroth argument is the name of the command itself,so we start to print with the first argument, until we've printed them all.Each .UL argv[i]is a character array, so we use a.UL `%s'in the.UL printf\*..PPYou will sometimes see the declaration of.UL argvwritten as.E1char \**argv[ ];.E2which is equivalent.But we can't use .UL char .UL argv[ .UL ][.UL ],because both dimensions are variable andthere would be no way to figureout how big the array is..PPHere's a bigger example using.UL argcand.UL argv\*.A common convention in C programs is that if the first argument is `\(mi',it indicates a flag of some sort.For example, suppose we want a program to be callable as.E1prog -abc arg1 arg2 \*.\*.\*..E2where the `\(mi' argument is optional;if it is present, it may be followed by any combination ofa, b, and c..E1main(argc, argv) int argc; char \**\**argv; { \*.\*.\*. aflag = bflag = cflag = 0; if( argc > 1 && argv[1][0] \*= '-' ) { for( i=1; (c=argv[1][i]) != '\\0'; i\*+ ) if( c\*='a' ) aflag\*+; else if( c\*='b' ) bflag\*+; else if( c\*='c' ) cflag\*+; else printf("%c?\\n", c); --argc; \*+argv; } \*.\*.\*..E2.PPThere are several things worth noticing about this code.First, there is a real need for the left-to-right evaluationthat && provides;we don't want to look at.UL argv[1]unless we know it's there.Second, the statements.E1 --argc; \*+argv;.E2let us march along the argument list by one position,so we can skip over the flag argument as if it had never existed _the rest of the program is independent ofwhether or not there was a flag argument.This only works because .UL argvis a pointer which can be incremented..NHThe Switch Statement; Break; Continue.PPThe.UL switchstatementcan be used to replace the multi-way testwe used in the last example.When the tests are like this:.E1if( c \*= 'a' ) \*.\*.\*.else if( c \*= 'b' ) \*.\*.\*.else if( c \*= 'c' ) \*.\*.\*.else \*.\*.\*..E2testing a value against a series of.ulconstants,theswitchstatement is often clearer and usually gives better code.Use it like this:.E1switch( c ) {case 'a': aflag\*+; break;case 'b': bflag\*+; break;case 'c': cflag\*+; break;default: printf("%c?\\n", c); break;}.E2The.UL casestatements label the various actions we want;.UL defaultgets done if none of the other cases are satisfied.(A.UL defaultis optional;if it isn't there,and none of the cases match,you just fall out the bottom.).PPThe.UL breakstatement in this example is new.It is therebecausethe cases are just labels,and after you do one of them,you.ulfall throughto the next unless you take some explicit action to escape.This is a mixed blessing.On the positive side,you can have multiple cases on a single statement;we might want to allow both upper and lower case letters in our flag field,so we could say.E1case 'a': case 'A': \*.\*.\*..SPcase 'b': case 'B': \*.\*.\*. etc\*..E2But what if we just want to get out after doing.UL case .UL `a'?We could get out of a.UL caseof the.UL switchwith a label and a.UL goto,but this is really ugly.The.UL breakstatement lets us exit without either.UL gotoor label..E1switch( c ) {case 'a': aflag\*+; break;case 'b': bflag\*+; break; \*.\*.\*.}/\** the break statements get us here directly \**/.E2The.UL breakstatementalso works in.UL forand.UL whilestatements _it causes an immediate exit from the loop..PPThe.UL continuestatement works.ulonlyinside.UL for'sand.UL while's;it causes the next iteration of the loop to be started.This means it goes to the increment part of the.UL forand the test part of the.UL while\*.We could have used a.UL continuein our example to get on with the next iterationof the.UL for,but it seems clearer to use.UL breakinstead.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -