confusion when comparing char * with a string literal

Discussion in 'C Programming' started by william, Mar 17, 2007.

  1. william

    william Guest

    below is a short piece of code I wrote to testify my understanding of
    char *, and array.

    #include <stdio.h>

    int main()
    {
    char *str=NULL;
    char x[]="today is good!";
    printf("%s", str);
    str=strtok(x," ");
    if (str=="today") //<==here is line that confuses me
    printf("they equals!\n");
    return 0;
    }

    I printed "str" first, and the console displayed "today". However,
    when I try to comapare 'str' with "today", the condition failed!

    Exactly speaking, I know that 'str' is a 4 byte pointer of char type,
    so it is not equal to a string. But even in the gdb, I used 'p str',
    it printed "today".

    So, my question is how do we compare arrays and char *(I found that it
    is so commonly used as string)?

    one more question: is it true that we have to initialize a char * or
    points it to somewhere in memory, or to malloc memory to it before we
    can use it?
    Because I got segfault several times arising from this problem too.

    Thank you for your reply in advance!

    Ji
    william, Mar 17, 2007
    #1
    1. Advertising

  2. william

    william Guest

    On Mar 17, 1:22 pm, "william" <> wrote:
    > below is a short piece of code I wrote to testify my understanding of
    > char *, and array.
    >
    > #include <stdio.h>
    >
    > int main()
    > {
    > char *str=NULL;
    > char x[]="today is good!";
    > printf("%s", str);
    > str=strtok(x," ");
    > if (str=="today") //<==here is line that confuses me
    > printf("they equals!\n");
    > return 0;
    > }
    >
    > I printed "str" first, and the console displayed "today". However,
    > when I try to comapare 'str' with "today", the condition failed!
    >
    > Exactly speaking, I know that 'str' is a 4 byte pointer of char type,
    > so it is not equal to a string. But even in the gdb, I used 'p str',
    > it printed "today".
    >
    > So, my question is how do we compare arrays and char *(I found that it
    > is so commonly used as string)?
    >
    > one more question: is it true that we have to initialize a char * or
    > points it to somewhere in memory, or to malloc memory to it before we
    > can use it?
    > Because I got segfault several times arising from this problem too.
    >
    > Thank you for your reply in advance!
    >
    > Ji



    By the way, gcc also reported:
    warning: assignment makes pointer from integer without a cast
    on the line: str=strtok(x," ");
    where I think the type is compatible :-(
    william, Mar 17, 2007
    #2
    1. Advertising

  3. william

    santosh Guest

    william wrote:
    > below is a short piece of code I wrote to testify my understanding of
    > char *, and array.
    >
    > #include <stdio.h>


    Include string.h to get the prototype for strtok.

    > int main()
    > {
    > char *str=NULL;
    > char x[]="today is good!";
    > printf("%s", str);


    Huh? You're passing a null pointer to printf and telling it to follow
    it? I think you meant to initilaise it to point to x.

    str = x;

    > str=strtok(x," ");


    strtok need a char * as it's first argument. x is a constant pointer.

    > if (str=="today") //<==here is line that confuses me


    You cannot directly compare strings in C. You'll need to compare them
    element by element. The Standard C library function for this is strcmp
    or strncmp.

    > printf("they equals!\n");
    > return 0;
    > }
    >
    > I printed "str" first, and the console displayed "today". However,
    > when I try to comapare 'str' with "today", the condition failed!
    >
    > Exactly speaking, I know that 'str' is a 4 byte pointer of char type,


    No. The Standard says nothing about the sizes of pointer types. Nor do
    you need to assume a particular size for what you're doing. And it's
    not a pointer *of* type char. It's a pointer *to* type char.

    > So, my question is how do we compare arrays and char *(I found that it
    > is so commonly used as string)?


    strcmp/strncmp/memcmp

    > one more question: is it true that we have to initialize a char * or
    > points it to somewhere in memory, or to malloc memory to it before we
    > can use it?


    Yes.

    > Because I got segfault several times arising from this problem too.


    Indeed you'll if you work on pointers that don't point to any valid
    object.
    santosh, Mar 17, 2007
    #3
  4. william

    Ben Pfaff Guest

    "santosh" <> writes:

    > william wrote:
    >> char *str=NULL;
    >> char x[]="today is good!";
    >> printf("%s", str);

    >
    >> str=strtok(x," ");

    >
    > strtok need a char * as it's first argument. x is a constant pointer.


    You're off-base here. There's nothing wrong with passing x to
    strtok as the first argument (modulo all the problems that are
    endemic with strtok anyhow).

    By the way: x is an array, not a constant pointer.
    --
    "I hope, some day, to learn to read.
    It seems to be even harder than writing."
    --Richard Heathfield
    Ben Pfaff, Mar 17, 2007
    #4
  5. william

    Chris Dollin Guest

    william wrote:

    > below is a short piece of code I wrote to testify my understanding of
    > char *, and array.
    >
    > #include <stdio.h>
    >
    > int main()
    > {
    > char *str=NULL;
    > char x[]="today is good!";
    > printf("%s", str);
    > str=strtok(x," ");
    > if (str=="today") //<==here is line that confuses me
    > printf("they equals!\n");
    > return 0;
    > }
    >
    > I printed "str" first, and the console displayed "today". However,
    > when I try to comapare 'str' with "today", the condition failed!
    >
    > Exactly speaking, I know that 'str' is a 4 byte pointer of char type,


    The number of bytes is takes up isn't portable.

    > so it is not equal to a string. But even in the gdb, I used 'p str',
    > it printed "today".


    gdb (which is off-topic) is being helpful.

    > So, my question is how do we compare arrays and char *(I found that it
    > is so commonly used as string)?


    strcmp().

    > one more question: is it true that we have to initialize a char * or
    > points it to somewhere in memory, or to malloc memory to it before we
    > can use it?


    Yes.

    If it's declared as a pointer, it should point /somewhere/ (or be
    null) to make sense using it.

    > Because I got segfault several times arising from this problem too.


    You were lucky. (Unlucky would be it /not/ faulting, until what you
    though was a working program fell over mysteriously while demonstrating
    to an Important Customer in the presence of Godlike Manager.)

    --
    Far-Fetched Hedgehog
    "Who do you serve, and who do you trust?" /Crusade/
    Chris Dollin, Mar 17, 2007
    #5
  6. william

    Chris Dollin Guest

    william wrote:

    > on the line: str=strtok(x," ");
    > where I think the type is compatible :-(


    You didn't #include <string.h>, so there's no declaration for
    `strtok`, so the compiler assumes return-type `int`, which you
    can't convert to char* without a cast.

    /Do not/ put in a cast. Put in the #include.

    --
    Far-Fetched Hedgehog
    "It took a very long time, much longer than the most generous estimates."
    - James White, /Sector General/
    Chris Dollin, Mar 17, 2007
    #6
  7. "william" <> writes:
    > below is a short piece of code I wrote to testify my understanding of
    > char *, and array.
    >
    > #include <stdio.h>
    >
    > int main()
    > {
    > char *str=NULL;
    > char x[]="today is good!";
    > printf("%s", str);
    > str=strtok(x," ");
    > if (str=="today") //<==here is line that confuses me
    > printf("they equals!\n");
    > return 0;
    > }
    >
    > I printed "str" first, and the console displayed "today". However,
    > when I try to comapare 'str' with "today", the condition failed!
    >
    > Exactly speaking, I know that 'str' is a 4 byte pointer of char type,
    > so it is not equal to a string. But even in the gdb, I used 'p str',
    > it printed "today".


    Apparently gdb didn't print the actual value of str (which would be an
    address); it chose to display the string that it points to, which
    presumably is more useful.

    > So, my question is how do we compare arrays and char *(I found that it
    > is so commonly used as string)?


    use the strcmp() function, declared in <string.h>.

    > one more question: is it true that we have to initialize a char * or
    > points it to somewhere in memory, or to malloc memory to it before we
    > can use it?
    > Because I got segfault several times arising from this problem too.


    It depends on what you're using it for. If you're treating it as a
    pointer to a string, it needs to point to a string, which means you
    need to allocate memory to hold the string.

    There's another problem in your program; I'll explain in another
    followup.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Mar 17, 2007
    #7
  8. "william" <> writes:
    > On Mar 17, 1:22 pm, "william" <> wrote:
    >> below is a short piece of code I wrote to testify my understanding of
    >> char *, and array.
    >>
    >> #include <stdio.h>
    >>
    >> int main()
    >> {
    >> char *str=NULL;
    >> char x[]="today is good!";
    >> printf("%s", str);
    >> str=strtok(x," ");
    >> if (str=="today") //<==here is line that confuses me
    >> printf("they equals!\n");
    >> return 0;
    >> }

    [...]
    >
    > By the way, gcc also reported:
    > warning: assignment makes pointer from integer without a cast
    > on the line: str=strtok(x," ");
    > where I think the type is compatible :-(


    You know that strtok() returns a result of type char*, but how is the
    compiler supposed to know that? You haven't provided a declaration of
    the strtok() function, so when the compiler sees a call to it, it
    assumes (incorrectly, in this case), that it returns int. (That's in
    C90; the rules are more strict in C99.)

    The way to provide a declaration for strtok() is to add
    #include <string.h>
    to the top of your program.

    Whenever you use a library function, or any declaration in the
    library, you need to provide a #include directive for the header that
    declares it.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Mar 17, 2007
    #8
  9. william

    Guest

    On Mar 17, 10:25 pm, "william" <> wrote:
    > On Mar 17, 1:22 pm, "william" <> wrote:
    >
    >
    >
    > > below is a short piece of code I wrote to testify my understanding of
    > > char *, and array.

    >
    > > #include <stdio.h>

    >
    > > int main()
    > > {
    > > char *str=NULL;
    > > char x[]="today is good!";
    > > printf("%s", str);
    > > str=strtok(x," ");
    > > if (str=="today") //<==here is line that confuses me
    > > printf("they equals!\n");
    > > return 0;
    > > }

    >
    > > I printed "str" first, and the console displayed "today". However,
    > > when I try to comapare 'str' with "today", the condition failed!

    >
    > > Exactly speaking, I know that 'str' is a 4 byte pointer of char type,
    > > so it is not equal to a string. But even in the gdb, I used 'p str',
    > > it printed "today".

    >
    > > So, my question is how do we compare arrays and char *(I found that it
    > > is so commonly used as string)?

    >
    > > one more question: is it true that we have to initialize a char * or
    > > points it to somewhere in memory, or to malloc memory to it before we
    > > can use it?
    > > Because I got segfault several times arising from this problem too.

    >
    > > Thank you for your reply in advance!

    >
    > > Ji

    >
    > By the way, gcc also reported:
    > warning: assignment makes pointer from integer without a cast
    > on the line: str=strtok(x," ");
    > where I think the type is compatible :-(




    >>Why does it print "today" ? :

    char * str means ..u know ..: " that 'str' is a 4 byte pointer
    of char type"
    But what ur program does is it makes a pointer variable, to which u
    havn't given any memory address..so if u try to access the memory
    location pointed to by ur pointer ,it will access some random
    memory .. Here itz the place were u hav stored the array "today " ..If
    it accesses some memory location outside the limits alloted for ur
    program ,ur OS will hook it by saying 'segfault'..
    >>How can u compare arrays ? :

    Can't u use 'strcmp': in string.h ... U 'll need to compare
    each character seperately to compare two arrays...
    >>Is it necessary to initialize ? :

    Yes ... 'char * ' is just a pointer ..It has to point somewhere
    before u could access content from it..
    >>Wat's the error in str=strtok(x," ") ? :

    Special to gcc... In gcc u won't be able to change a char []
    declared unless u use --enable options as argument to gcc ...Moreover
    itz a warning leave it ...(I think so ..but to err is human)


    -Nf
    , Mar 17, 2007
    #9
  10. william

    Flash Gordon Guest

    wrote, On 17/03/07 19:29:
    > On Mar 17, 10:25 pm, "william" <> wrote:
    >> On Mar 17, 1:22 pm, "william" <> wrote:
    >>
    >>
    >>
    >>> below is a short piece of code I wrote to testify my understanding of
    >>> char *, and array.
    >>> #include <stdio.h>
    >>> int main()
    >>> {
    >>> char *str=NULL;
    >>> char x[]="today is good!";
    >>> printf("%s", str);
    >>> str=strtok(x," ");
    >>> if (str=="today") //<==here is line that confuses me
    >>> printf("they equals!\n");
    >>> return 0;
    >>> }
    >>> I printed "str" first, and the console displayed "today". However,
    >>> when I try to comapare 'str' with "today", the condition failed!
    >>> Exactly speaking, I know that 'str' is a 4 byte pointer of char type,
    >>> so it is not equal to a string. But even in the gdb, I used 'p str',
    >>> it printed "today".
    >>> So, my question is how do we compare arrays and char *(I found that it
    >>> is so commonly used as string)?
    >>> one more question: is it true that we have to initialize a char * or
    >>> points it to somewhere in memory, or to malloc memory to it before we
    >>> can use it?
    >>> Because I got segfault several times arising from this problem too.
    >>> Thank you for your reply in advance!
    >>> Ji

    >> By the way, gcc also reported:
    >> warning: assignment makes pointer from integer without a cast
    >> on the line: str=strtok(x," ");
    >> where I think the type is compatible :-(

    >
    >>> Why does it print "today" ? :

    > char * str means ..u know ..: " that 'str' is a 4 byte pointer
    > of char type"


    No it does not. It means str is a pointer to char, there is nothing to
    suggest how large the pointer is.

    Also, please don't use contractions like u and ur, it makes reading your
    post difficult, and more people read each post that write it.

    > But what ur program does is it makes a pointer variable, to which u
    > havn't given any memory address..so if u try to access the memory
    > location pointed to by ur pointer ,it will access some random
    > memory .. Here itz the place were u hav stored the array "today " ..If
    > it accesses some memory location outside the limits alloted for ur
    > program ,ur OS will hook it by saying 'segfault'..


    Or it might not. It could do anything.

    >>> How can u compare arrays ? :

    > Can't u use 'strcmp': in string.h ... U 'll need to compare
    > each character seperately to compare two arrays...
    >>> Is it necessary to initialize ? :

    > Yes ... 'char * ' is just a pointer ..It has to point somewhere
    > before u could access content from it..
    >>> Wat's the error in str=strtok(x," ") ? :

    > Special to gcc...


    No it is not.

    > In gcc u won't be able to change a char []


    Complete rubbish. A char array is modifiable unless declared as const,
    string literals are a completely different matter (you are not allowed
    to modify them) but since the OP did NOT try to modify a string literal
    they are not relevant.

    > declared unless u use --enable options as argument to gcc ...Moreover
    > itz a warning leave it ...(I think so ..but to err is human)


    Incredibly stupid advice. The warning was due to a very serious error,
    one whic *will* cause the program to fail on some modern C
    implementations. Namely failing to provide a prototype for strtok, and
    the best solution to this is including the standard header that provides it.

    Ignoring warnings is NEVER a good idea. Very occasionally it makes sense
    to leave in a warning, but only when you have enough experience to know
    *why* you are write in that specific situation.
    --
    Flash Gordon
    Flash Gordon, Mar 17, 2007
    #10
  11. william

    Joe Wright Guest

    Ben Pfaff wrote:
    > "santosh" <> writes:
    >
    >> william wrote:
    >>> char *str=NULL;
    >>> char x[]="today is good!";
    >>> printf("%s", str);
    >>> str=strtok(x," ");

    >> strtok need a char * as it's first argument. x is a constant pointer.

    >
    > You're off-base here. There's nothing wrong with passing x to
    > strtok as the first argument (modulo all the problems that are
    > endemic with strtok anyhow).
    >
    > By the way: x is an array, not a constant pointer.


    Once strtok gets it, x is a char* with no indication whether it started
    out as an array of char or a pointer to constant string.

    How tough is it living in Palo Alto? :=)

    --
    Joe Wright
    "Everything should be made as simple as possible, but not simpler."
    --- Albert Einstein ---
    Joe Wright, Mar 18, 2007
    #11
  12. william

    Ben Pfaff Guest

    Joe Wright <> writes:

    > How tough is it living in Palo Alto? :=)


    Very, very expensive.
    --
    "In My Egotistical Opinion, most people's C programs should be indented six
    feet downward and covered with dirt." -- Blair P. Houghton
    Ben Pfaff, Mar 18, 2007
    #12
  13. william

    Joe Wright Guest

    Ben Pfaff wrote:
    > Joe Wright <> writes:
    >
    >> How tough is it living in Palo Alto? :=)

    >
    > Very, very expensive.


    I left the area 15 years ago and it was expensive then. Now I live in
    Northern Virginia within stone's throw of the Pentagon. How expensive is
    it? A decent meal for two costs between 60 and 160 dollars (or more).

    The average drink is 5 or 6 dollars in any old bar. Is it worse than
    that in Silicon Valley?

    --
    Joe Wright
    "Everything should be made as simple as possible, but not simpler."
    --- Albert Einstein ---
    Joe Wright, Mar 18, 2007
    #13
  14. Joe Wright said:

    > Now I live in Northern Virginia within stone's throw of the Pentagon.


    Nevertheless, I recommend that you resist the temptation.

    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/1999
    http://www.cpax.org.uk
    email: rjh at the above domain, - www.
    Richard Heathfield, Mar 18, 2007
    #14
  15. william

    Joe Wright Guest

    Richard Heathfield wrote:
    > Joe Wright said:
    >
    >> Now I live in Northern Virginia within stone's throw of the Pentagon.

    >
    > Nevertheless, I recommend that you resist the temptation.
    >

    Not to worry. There are no stones within stone's throw of the Pentagon.
    Security, I suppose. :)

    --
    Joe Wright
    "Everything should be made as simple as possible, but not simpler."
    --- Albert Einstein ---
    Joe Wright, Mar 18, 2007
    #15
  16. william

    CBFalconer Guest

    Joe Wright wrote:
    > Richard Heathfield wrote:
    >> Joe Wright said:
    >>
    >>> Now I live in Northern Virginia within stone's throw of the Pentagon.

    >>
    >> Nevertheless, I recommend that you resist the temptation.

    >
    > Not to worry. There are no stones within stone's throw of the Pentagon.
    > Security, I suppose. :)


    I imagine getting caught smuggling contraband stones to within a
    stones throw of the Pentagon will get you thrown into the Gitmo
    gulag for an indefinite stay in stone cells, together with stony
    inquisitions.

    --
    Chuck F (cbfalconer at maineline dot net)
    Available for consulting/temporary embedded and systems.
    <http://cbfalconer.home.att.net>



    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Mar 18, 2007
    #16
  17. william

    william Guest

    On Mar 17, 6:06 pm, Flash Gordon <> wrote:
    > wrote, On 17/03/07 19:29:
    >
    >
    >
    > > On Mar 17, 10:25 pm, "william" <> wrote:
    > >> On Mar 17, 1:22 pm, "william" <> wrote:

    >
    > >>> below is a short piece of code I wrote to testify my understanding of
    > >>> char *, and array.
    > >>> #include <stdio.h>
    > >>> int main()
    > >>> {
    > >>> char *str=NULL;
    > >>> char x[]="today is good!";
    > >>> printf("%s", str);
    > >>> str=strtok(x," ");
    > >>> if (str=="today") //<==here is line that confuses me
    > >>> printf("they equals!\n");
    > >>> return 0;
    > >>> }
    > >>> I printed "str" first, and the console displayed "today". However,
    > >>> when I try to comapare 'str' with "today", the condition failed!
    > >>> Exactly speaking, I know that 'str' is a 4 byte pointer of char type,
    > >>> so it is not equal to a string. But even in the gdb, I used 'p str',
    > >>> it printed "today".
    > >>> So, my question is how do we compare arrays and char *(I found that it
    > >>> is so commonly used as string)?
    > >>> one more question: is it true that we have to initialize a char * or
    > >>> points it to somewhere in memory, or to malloc memory to it before we
    > >>> can use it?
    > >>> Because I got segfault several times arising from this problem too.
    > >>> Thank you for your reply in advance!
    > >>> Ji
    > >> By the way, gcc also reported:
    > >> warning: assignment makes pointer from integer without a cast
    > >> on the line: str=strtok(x," ");
    > >> where I think the type is compatible :-(

    >
    > >>> Why does it print "today" ? :

    > > char * str means ..u know ..: " that 'str' is a 4 byte pointer
    > > of char type"

    >
    > No it does not. It means str is a pointer to char, there is nothing to
    > suggest how large the pointer is.
    >
    > Also, please don't use contractions like u and ur, it makes reading your
    > post difficult, and more people read each post that write it.
    >
    > > But what ur program does is it makes a pointer variable, to which u
    > > havn't given any memory address..so if u try to access the memory
    > > location pointed to by ur pointer ,it will access some random
    > > memory .. Here itz the place were u hav stored the array "today " ..If
    > > it accesses some memory location outside the limits alloted for ur
    > > program ,ur OS will hook it by saying 'segfault'..

    >
    > Or it might not. It could do anything.
    >
    > >>> How can u compare arrays ? :

    > > Can't u use 'strcmp': in string.h ... U 'll need to compare
    > > each character seperately to compare two arrays...
    > >>> Is it necessary to initialize ? :

    > > Yes ... 'char * ' is just a pointer ..It has to point somewhere
    > > before u could access content from it..
    > >>> Wat's the error in str=strtok(x," ") ? :

    > > Special to gcc...

    >
    > No it is not.
    >
    > > In gcc u won't be able to change a char []

    >
    > Complete rubbish. A char array is modifiable unless declared as const,
    > string literals are a completely different matter (you are not allowed
    > to modify them) but since the OP did NOT try to modify a string literal
    > they are not relevant.
    >
    > > declared unless u use --enable options as argument to gcc ...Moreover
    > > itz a warning leave it ...(I think so ..but to err is human)

    >
    > Incredibly stupid advice. The warning was due to a very serious error,
    > one whic *will* cause the program to fail on some modern C
    > implementations. Namely failing to provide a prototype for strtok, and
    > the best solution to this is including the standard header that provides it.
    >
    > Ignoring warnings is NEVER a good idea. Very occasionally it makes sense
    > to leave in a warning, but only when you have enough experience to know
    > *why* you are write in that specific situation.
    > --


    Thank you for this piece of advice: "DO NOT ingore warnings"
    > Flash Gordon
    william, Mar 19, 2007
    #17
  18. william

    william Guest

    Thank you, everyone speaking here. Thank you for your replies and
    effort, which helped me to understand these portion of C much better.

    and sorry for multi-post in both c/c++ groups.
    william, Mar 19, 2007
    #18
  19. william

    CBFalconer Guest

    william wrote:
    >

    .... snip ...
    >
    > Thank you for this piece of advice: "DO NOT ingore warnings"


    No, I recommend exgoreing them. Alternatively, ask Al.

    --
    Chuck F (cbfalconer at maineline dot net)
    Available for consulting/temporary embedded and systems.
    <http://cbfalconer.home.att.net>


    --
    Posted via a free Usenet account from http://www.teranews.com
    CBFalconer, Mar 19, 2007
    #19
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Zach
    Replies:
    3
    Views:
    5,522
    Mike Wahler
    Dec 12, 2004
  2. , India

    Comparing char * with string literal

    , India, Mar 8, 2007, in forum: C Programming
    Replies:
    9
    Views:
    499
    Malcolm McLean
    Mar 8, 2007
  3. william
    Replies:
    10
    Views:
    754
    Gavin Deane
    Mar 19, 2007
  4. kaoruAngel

    Set literal confusion

    kaoruAngel, Sep 22, 2009, in forum: Python
    Replies:
    5
    Views:
    353
    Dennis Lee Bieber
    Sep 23, 2009
  5. Anonieko Ramos

    What's wrong with rpc-literal? Why use doc-literal?

    Anonieko Ramos, Sep 27, 2004, in forum: ASP .Net Web Services
    Replies:
    0
    Views:
    363
    Anonieko Ramos
    Sep 27, 2004
Loading...

Share This Page