As was foretold, we've added advertisements to the forums! If you have questions, or if you encounter any bugs, please visit this thread: https://forums.penny-arcade.com/discussion/240191/forum-advertisement-faq-and-reports-thread/
Options

(Hopefully) Simple C syntax question

Steel AngelSteel Angel Registered User regular
edited March 2008 in Help / Advice Forum
So it's been a few years since I've done any significant programming in C or C++ so my syntax is a bit rusty.

I'm going to be adding some system calls to a linux kernel, some of which will accept a character pointer (that points to an array of characters) as an argument.

My old C book sucks and barely covers working with character arrays and it's been many, many semesters since I had my C course notes bookmarked. All I remember was having issues with when I needed a * and & so I'm hoping for some help with syntax to do the following:

1) The prototype that goes in the .h file.
2) The declaration that goes into the .c file I'll be writing out the function in.

Google only seems to turn up stuff that doesn't involve passing this stuff to functions.

On a side note, if anyone can think of a good replacement for my old, sucky C book, feel free to name them.

Big Dookie wrote: »
I found that tilting it doesn't work very well, and once I started jerking it, I got much better results.

Steam Profile
3DS: 3454-0268-5595 Battle.net: SteelAngel#1772
Steel Angel on

Posts

  • Options
    VThornheartVThornheart Registered User regular
    edited February 2008
    If I remember correctly... when you pass an array into a function, what you're really passing is a pointer to the first member of the array.

    So if you had:

    void someFunction(char * arrayChar);

    I *think* (correct me if I'm wrong, it's been a long time since I worked with a language that uses pointers directly) you can do all of the pointer math on that "arrayChar" variable passed in to get to the subsequent members:

    arrayChar[5] (6th position in the array)

    etc...

    IMPORTANT NOTE: Someone correct me if I'm wrong. It's been a long, long time. I'm giving from the best that I can remember, so I apologize in advance if it's incorrect. But give it a shot, try it with a small test function and see if it works as advertised. Then report back here.

    VThornheart on
    3DS Friend Code: 1950-8938-9095
  • Options
    DocDoc Registered User, ClubPA regular
    edited February 2008
    the method definition/declaration should look like this:

    void methodName(char* argPointer)


    Then to call it:

    char* newString = new char[50]; //50-character long string, null terminator included
    newString = "this is a test";
    methodName(newString);

    Doc on
  • Options
    VThornheartVThornheart Registered User regular
    edited February 2008
    Doc wrote: »
    the method definition/declaration should look like this:

    void methodName(char* argPointer)


    Then to call it:

    char* newString = new char[50]; //50-character long string, null terminator included
    newString = "this is a test";
    methodName(newString);

    Ah, good, I wasn't crazy then. I posted it thinking it was right, but I was having some severe self-doubt about it.

    VThornheart on
    3DS Friend Code: 1950-8938-9095
  • Options
    ecco the dolphinecco the dolphin Registered User regular
    edited February 2008
    Doc wrote: »
    void methodName(char* argPointer)

    Don't mean to sound an*l retentive, but you will need a semi-colon behind that. Else the compiler will give you an error, most likely on the next non-blank line.

    Also, I'm afraid you've written C++ (new is a C++ keyword). =/ As far as I'm aware, the Linux kernel is written entirely in C, with no C++, so it may be best to avoid C++ syntax. String assignment with the equals sign is not legal either (I can't remember exactly, but at the very least you're passing a const char * to a char *, thus attempting to discard the const).

    Anyway, here's an example in C:

    test.h:
    void testArray(unsigned char *pCharArray); // can be unsigned char * or signed char *
    

    test.c:
    #include "test.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[])
    {
      unsigned char nSingleByte = 'a';
      unsigned char nArrayOfBytes[35];
    
      nArrayOfBytes[0] = 'd';
    
      // Pass the address of nSingleByte
      testArray(&nSingleByte); // Output: a a
    
      // The variable nArrayOfBytes itself is a pointer to the first element of the array,
      // so you can pass it without a * or &
      testArray(nArrayOfBytes); // Output: d d
    }
    
    // the "unsigned char *" here should be read "a variable called pCharArray which is a pointer to at least one unsigned char"
    void testArray(unsigned char *pCharArray)
    {
      // The 0th (first) element of the potential array pointed to by pCharArray
      // You do not know if pCharArray points to a single "unsigned char", or an array (and how
      // large the array is)
      printf("&#37;c ", pCharArray[0]);
    
      // The unsigned char at the address that pCharArray points to
      printf("%c\n", *pCharArray); // Functionally same thing as above for character arrays
    
      // You can access later elements by pCharArray[n] for the n+1'th element, or
      // *(pCharArray + n)
    }
    
    

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • Options
    DocDoc Registered User, ClubPA regular
    edited February 2008
    eecc wrote: »
    Doc wrote: »
    void methodName(char* argPointer)

    Don't mean to sound an*l retentive, but you will need a semi-colon behind that. Else the compiler will give you an error, most likely on the next non-blank line.

    Depends on if it's the definition or the declaration. :P

    Doc on
  • Options
    DocDoc Registered User, ClubPA regular
    edited February 2008
    For &, just remember that it's the "address of" operator. It gives you the address of (a pointer to) whatever you put it in front of.

    So this is valid:

    char test = 'a';
    char* testptr = &test;

    Now test is 'a', and testptr is a pointer to it.

    Makes sense?

    Doc on
  • Options
    Steel AngelSteel Angel Registered User regular
    edited February 2008
    Doc wrote: »
    For &, just remember that it's the "address of" operator. It gives you the address of (a pointer to) whatever you put it in front of.

    So this is valid:

    char test = 'a';
    char* testptr = &test;

    Now test is 'a', and testptr is a pointer to it.

    Makes sense?

    I have no issue when I'm referencing/dereferencing something in the same scope as declaring it, it's when it gets passed in from another source that it starts to get confusing. Like say:

    void SomeFunction (char *testptr) {
    do stuff with testptr;
    }

    That's when I start to get confused.

    Steel Angel on
    Big Dookie wrote: »
    I found that tilting it doesn't work very well, and once I started jerking it, I got much better results.

    Steam Profile
    3DS: 3454-0268-5595 Battle.net: SteelAngel#1772
  • Options
    ASimPersonASimPerson Cold... and hard.Registered User regular
    edited February 2008
    Doc wrote: »
    For &, just remember that it's the "address of" operator. It gives you the address of (a pointer to) whatever you put it in front of.

    So this is valid:

    char test = 'a';
    char* testptr = &test;

    Now test is 'a', and testptr is a pointer to it.

    Makes sense?

    I have no issue when I'm referencing/dereferencing something in the same scope as declaring it, it's when it gets passed in from another source that it starts to get confusing. Like say:

    void SomeFunction (char *testptr) {
    do stuff with testptr;
    }

    That's when I start to get confused.

    It depends on what testptr is.

    If testptr is a character array (i.e., a string), then you pass it it without taking the address, since it already is a pointer. For instance:
    void SomeFunction (char *str) {
        // do stuff
    }
    
    void AnotherFunction () {
       char *my_string = "This is a string!";
    
       SomeFunction(my_string);
    }
    

    However, there are situations where you may want to pass something as a pointer that isn't a pointer to start with. A common example in C is for functions that return error codes or some such, and need to modify the values passed in as arguments. For instance:
    void foo (int a) {
        a = 4;
    }
    
    void bar() {
        int test = 5;
        foo(test);
        printf("&#37;d\n", test);
    }
    
    Will print out "5" to the console, since the value of "test" was not modified outside the scope of foo. However, if we change the example to this:
    void foo (int *a) {
        *a = 4;
    }
    
    void bar() {
        int test = 5;
        foo(&test);
        printf("%d\n", test);
    }
    
    This will print "4". Why? We passed a pointer to test to foo. foo then dereferenced the pointer using the * operator, i.e., it sets the value of the thing pointed to by a to 4. Since the thing pointed to by a is test, the value of test in memory was modified.

    This all gets really fun when you need to modify the value of strings passed to a function, in which case you get into the land of double pointers.

    Um, does this make any sense?

    ASimPerson on
  • Options
    ASimPersonASimPerson Cold... and hard.Registered User regular
    edited February 2008
    eecc wrote: »
    As far as I'm aware, the Linux kernel is written entirely in C, with no C++, so it may be best to avoid C++ syntax. String assignment with the equals sign is not legal either (I can't remember exactly, but at the very least you're passing a const char * to a char *, thus attempting to discard the const)

    It is true Doc gave a C++ example, but I'm not entirely sure what this has to do with the Linux kernel. There are plenty of reasons to write in C or C++, but the language the kernel is in isn't one of them, unless there's a discussion about what language is appropriate for systems programming that I missed. :)

    ASimPerson on
  • Options
    DocDoc Registered User, ClubPA regular
    edited February 2008
    eecc wrote: »
    String assignment with the equals sign is not legal either (I can't remember exactly, but at the very least you're passing a const char * to a char *, thus attempting to discard the const).

    Yeah, that's true. Too much time with Python has caused me to start using other languages in invalid ways, heh.

    Doc on
  • Options
    ASimPersonASimPerson Cold... and hard.Registered User regular
    edited February 2008
    Doc wrote: »
    eecc wrote: »
    String assignment with the equals sign is not legal either (I can't remember exactly, but at the very least you're passing a const char * to a char *, thus attempting to discard the const).

    Yeah, that's true. Too much time with Python has caused me to start using other languages in invalid ways, heh.

    Not entirely. It is legal to initialize a string to a constant string (like in my example), but that's the only place.

    ASimPerson on
  • Options
    DaenrisDaenris Registered User regular
    edited February 2008
    ASimPerson wrote: »
    eecc wrote: »
    As far as I'm aware, the Linux kernel is written entirely in C, with no C++, so it may be best to avoid C++ syntax. String assignment with the equals sign is not legal either (I can't remember exactly, but at the very least you're passing a const char * to a char *, thus attempting to discard the const)

    It is true Doc gave a C++ example, but I'm not entirely sure what this has to do with the Linux kernel. There are plenty of reasons to write in C or C++, but the language the kernel is in isn't one of them, unless there's a discussion about what language is appropriate for systems programming that I missed. :)

    In the original post Steel Angel mentioned he was going to be adding some system calls to the Linux kernel.

    Daenris on
  • Options
    ASimPersonASimPerson Cold... and hard.Registered User regular
    edited February 2008
    In that case, I did miss the discussion on kernel programming. :)

    ASimPerson on
  • Options
    ecco the dolphinecco the dolphin Registered User regular
    edited February 2008
    I have no issue when I'm referencing/dereferencing something in the same scope as declaring it, it's when it gets passed in from another source that it starts to get confusing. Like say:

    void SomeFunction (char *testptr) {
    do stuff with testptr;
    }

    That's when I start to get confused.

    Are you able to give us an example of what you'd like to do with the pointer? I'm sure many of us can show you how to do it and explain why - maybe that'll help better than us giving you advice on general pointer usage?

    ASimPerson wrote: »
    Not entirely. It is legal to initialize a string to a constant string (like in my example), but that's the only place.

    While true, it is a bit dangerous since the pointer is to a const string. Attempts to modify it (not unreasonable when someone gives you a char *) will cause the programme to segfault/generate an access violation. For example, this programme crashes:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[])
    {
      char *pString = "hello";
    
      pString[3] = 'f';
    
      printf("%s\n", pString);
    
      return 0;
    }
    

    What you may have meant may be:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[])
    {
      char pString[] = "hello";
    
      pString[3] = 'f';
    
      printf("%s\n", pString);
    
      return 0;
    }
    

    where pString is now a string array initialised with the string "hello" instead of a char * to a const string.
    Doc wrote: »
    eecc wrote: »
    String assignment with the equals sign is not legal either (I can't remember exactly, but at the very least you're passing a const char * to a char *, thus attempting to discard the const).

    Yeah, that's true. Too much time with Python has caused me to start using other languages in invalid ways, heh.

    To be fair, that would've been legal in C++ had you used the std::string class. =P

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
  • Options
    Steel AngelSteel Angel Registered User regular
    edited February 2008
    eecc wrote: »
    Are you able to give us an example of what you'd like to do with the pointer? I'm sure many of us can show you how to do it and explain why - maybe that'll help better than us giving you advice on general pointer usage?

    Nothing horribly complex. I'm basically taking a character array and an integer n, then copying the first n values of the passed in array into one I'll dynamically allocate. The allocated array is going to be declared using extern so I don't have to return a pointer to it (I need to return an numerical value anyway just so I can pass back an error code if stuff goes wrong)

    So I expect to have something that looks like this:
    long FunctionName (char *character_array, long word_length) {
       do stuff using (m/k)alloc and strcpy;
       return 0;
    }
    

    So I'll need to know the syntax for dealing with the array being passed in by reference and what, if any, use of * I'll need during the strcpy.

    Steel Angel on
    Big Dookie wrote: »
    I found that tilting it doesn't work very well, and once I started jerking it, I got much better results.

    Steam Profile
    3DS: 3454-0268-5595 Battle.net: SteelAngel#1772
  • Options
    ecco the dolphinecco the dolphin Registered User regular
    edited March 2008
    Oh. Right. You wouldn't need to dereference anything if all you're doing is copying - you just need the pointers.

    So in that case, you'll want something that goes vaguely like:
    long functionName(char *pArray, long nLength)
    {
      g_pAllocatedArray = allocate_memory_and_free_previous_memory_if_needs_be(nLength);
      // g_pAllocatedArray is your extern char *
    
      // Copies nLength chars from the area of memory pointed to by pArray
      // to the area of memory pointed to by g_pAllocatedArray
      memcpy(g_pAllocatedArray, pArray, nLength);
    
      return 0;
    }
    

    You typically wouldn't use strcpy in this application as it copies data until it hits a null terminator, irrespective of length. Could be the cause of a buffer overflow.

    You would use either:

    1.) memcpy (does not assume that the source data is an ASCII string, and will copy exactly nLength bytes)

    or

    2.) strncpy (assumes that the source data is a string, but has an option for the maximum number of characters to copy - it can always copy less if the string is less than the specified maximum number of characters)

    depending on application.

    ecco the dolphin on
    Penny Arcade Developers at PADev.net.
Sign In or Register to comment.