Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Twiggy Johnson posted:

My google-fu is failing me for this one:

I have C program that calls FORTRAN, which then fills an array (pltstore) based on calculations. Currently, the C portion does nothing with pltstore beyond declare it as part of a global struct. I want to access pltstore from C after Fortran fills it.

C, in main.c:
code:
struct {
  float pltstore[40000];
  int nraystore[NRAYMAX],markray[NRAYMAX],npltray;
} plotvar_;
FORTRAN, in header file:
code:
 real pltstore
 common /plotvar/ pltstore(2,5000,4),nraystore(NRAYMAX)
&,markray(NRAYMAX),npltray
How do I convert the indexing between Fortran's 3D array and C's 1D array?

I tried declaring in C with
code:
float pltstore[4][5000][2];
but that gave me a segmentation fault when I tried accessing.

Dammit, does that explanation even make sense?
If I had to guess, there may be predictable pattern where elements get put into array from cobol. Since it's x y z, something like z*5000*2 + y * 2 + z....

Adbot
ADBOT LOVES YOU

RichardA
Sep 1, 2006
.
Dinosaur Gum
In c wouldn't the struct need to be extern?
edit: Nevermind. Wasn't reading closely.

RichardA fucked around with this message at 23:57 on Nov 16, 2011

Damiscus
Apr 8, 2008

..!
Basic programming questions:

I'm trying to write a function (12 months of the year) that takes an enumeration constant and returns the next month in the year, and a second function that takes an enumeration constant and an int and increments the months by the int (figure it'd just use the function to get the next month int number of times) and returns the new month.

How do I get it to wrap around back to January?

I have a function already that accepts an enumeration constant and prints the month that it corresponds to.

---

Also, with the same months, if I'm writing an application to compare two months and tell which is later in the year (user inputs two months, if the same nothing happens, if one is later in the year it says so, and january > december), what's the most efficient way to do the comparisons? A massive if/else or switch statement seems clumsy.

Thank you!

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!
The modulus operator is good for both of these tasks, and would mean you don't need to call your "add one month" function a bunch of times. The percent sign is what you use for a modulus. To convert to months, you'd use it like n%12 - zero would be January, 11 would be December, you should probably force the enumerated constants into being that sequence of numbers. (12%12 would loop back to zero, as would 24%12.)

If you can't rely on the enumerated values being sequential then you'd be stuck with a lot of if/else or a switch - the only thing you could do to keep from a hundred "add one month" calls then would be that you could mod-12 the number of months you need to add before you start.

It sounds like you haven't got a very well defined criterion for "later in the year" if January>December (Is June>December? July?). You need to decide on the logic before you worry about the programming.

roomforthetuna fucked around with this message at 01:06 on Nov 17, 2011

Damiscus
Apr 8, 2008

..!
Thank you, the modulus operator is what I needed for the first part. I had only used it to find only even or odd numbers before, didn't think to use it like this.

Any input on the second question?

pseudorandom name
May 6, 2007

It's a straightforward numerical comparison unless the month is December.

Damiscus
Apr 8, 2008

..!
http://codepad.org/6FjaPoJd

Completely butchering this, I know. How would I apply the modulus operator and/or un**** this code?

delta534
Sep 2, 2011

Damiscus posted:

http://codepad.org/6FjaPoJd

Completely butchering this, I know. How would I apply the modulus operator and/or un**** this code?

You can fix the code by understanding that enums are implicitly cast to ints. So the next day function could be
code:
d_t next_day(d_t current)
{
    return (current+1)%7;
}
and the many days function could be
code:
d_t many_days(d_t current,int number)
{
    return (current+number)%7;
}

nielsm
Jun 1, 2009



^^ I knew someone would write a shorter answer first ;)

Damiscus posted:

http://codepad.org/6FjaPoJd

Completely butchering this, I know. How would I apply the modulus operator and/or un**** this code?

First, that code can't work as it is, there are a few problems, mainly in many_days(). You never initialize the currentday variable before you pass it into next_day(), and you never feed the value returned from next_day() back into the loop, it just gets discarded after every loop. The result is that you continually take the next day of the same uninitialized value. You also declare the argument of the function as d_t although it can clearly be values outside the enumeration, from how the function is used.
A better way to design the many_days() function would be to have it take two parameters: a d_t telling the current day of the week, and an int telling how many days to travel, then return a d_t telling the new day of the week.

It may not have been covered (yet) in your classes or book, or whatever you are learning from, but in C, you can also explicitly declare the integral values of names in an enum, like this:
code:
typedef enum {
  monday = 1,
  tuesday,
  wednesday,
  thursday,
  friday,
  saturday,
  sunday
} d_t;
This will make monday have value 1, tuesday have value 2, and so on. (Names not explicitly given a value always get the value of the previous plus one.) However that won't really help in this case anyway, because you need to be zero-based for modulus arithmetic to work properly.

code:
/* assuming d_t is zero-based */
d_t many_days(d_t currentday, int daystotravel)
{
  int numericday;
  d_t resultingday;
  /* start by setting the current numeric day */
  numericday = currentday;
  /* add the number of days to travel, this will also work if the number is negative */
  numericday += daystotravel;
  /* modulus 7 is basically subtracting 7 until the number is less than 7:
     the remainder of an integer division.
     this brings the value into the correct range, and it can safely be cast
     back to a d_t value. */
  resultingday = (d_t)(numericday % 7);
  return resultingday;
}

Walked
Apr 14, 2003

I've been programming in C# for a while now. Not professionally (well, it gets used for automation tasks, but I'm not a developer).

That said, I've been meaning to dive into C++ a bit to expand horizons / understand more / work with some lower level stuff.

1) Is there a good C++ primer for people who understand OOP and general programming concepts? (specifically .net, but whatever)

2) Is there a good moderate (one to two days) sized project someone can recommend as a good introductory thing to C++?

3) Is it just me, or does intellisense for C++ suck in VS2010, coming from C#? God drat. I mean, it's there, but drat. :argh:

Walked fucked around with this message at 15:20 on Nov 17, 2011

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

Walked posted:

3) Is it just me, or does intellisense for C++ suck in VS2010, coming from C#? God drat. I mean, it's there, but drat. :argh:
It's terrible compared to C#. Visual Assist X makes it significantly less bad.

Twiggy Johnson
Jun 10, 2011

roomforthetuna posted:

How did you try accessing it? The three-dimensional declaration should be fine if you access it in the same manner. (eg. pltstore[0][0][0]=0.0f;) Unless the fortran array is not the same size.

If it's more convenient to do it one-dimensionally (as is the case if you allocate it dynamically) then you can index it like thing[x*(ysize*zsize) + y*zsize + z].

(Then you can test to see if your one-dimensional array is indexed in the right order to map to the fortran one, and reorder it if necessary.)

Whoops, turns out the segmentation fault was being caused by something else. The 3D indexing was working as expected. Thanks.

HFX
Nov 29, 2004

Plorkyeran posted:

It's terrible compared to C#. Visual Assist X makes it significantly less bad.

Sadly, intellesense is better in visual studio then similar features are in most other C++ IDEs.

Twiggy Johnson
Jun 10, 2011
C++ question this time:
code:
struct stats
{
  (members)
  ...
  vector<stats*> detected;

  void update(vector<stats>&);
}

void stats::update(vector<stats> &active)
{
  detected.clear();
  detected.resize(active.size());

  for (unsigned int i = 0; i < active.size(); i++)
    detected[i] = &active[i];
}
Compiles with mingw C++11. Runs sometimes, but usually crashes when it gets to detected.resize(). Same behavior if I use push_back, or try resizing to a constant. Any ideas?

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
The stats object you're working with is corrupted. It's probably a dangling pointer to an object from a vector that was resized or destroyed.

nielsm
Jun 1, 2009



rjmccall posted:

The stats object you're working with is corrupted. It's probably a dangling pointer to an object from a vector that was resized or destroyed.

This is likely.
It's very risky to use pointers into vectors that change.

Step through it with a debugger, preferably use a debugging memory allocator (that writes specific patterns to freshly allocated memory and others to deallocated memory) so you can easily tell what is being pointed to.

Twiggy Johnson
Jun 10, 2011
The object's getting corrupted because I'm trying to write and read a struct with an embedded vector to a binary file. This is the second time I've learned that doesn't work. :eng99:

some bust on that guy
Jan 21, 2006

This avatar was paid for by the Silent Majority.
I've got a C question about Linked Lists.

What does the "nested functions are disabled, use -fnested-functions to re-enable" error mean in C?

I'm trying to return a copy of a linked list. The copy function should take in no arguments but return a copied list. It doesn't seem to like me setting the first variable to NULL when I create a new linked list to store the copied LL in. But I don't know how else to do it.

Probably something to do with pointers. I'm not good with pointers.

code:
struct node
{
    int element;
    struct node *next;
    struct node *first;
    struct node *last;
};

struct node *first = (struct node *) NULL;   //This list
struct node *last = (struct node *) NULL;
int size = 0;

....

struct node *copy() {
    struct node *result = NULL;
    struct node result->first = NULL; //This is where the error happens.
    int index = 0;
    while (index < size) {
	struct node *p = first;
        int temp = first->element;
        int pos = 0;
        while (pos != index) {
	    temp = p->next->element;
	    p = p->next;
	    pos = pos + 1;
          }
	addToOther(result, temp);
        index = index + 1;
     }
     return result;
    }

...

main() 
{
  
  struct set *s = (struct set *) malloc(sizeof(struct set));
  s->size = 0;
  add(1);  //adds to this list
  add(3);    
  add(4);
  struct node *r = copy();  //Stores copy in result list.  This is where the program breaks.

tractor fanatic
Sep 9, 2005

Pillbug
It means you are missing a brace somewhere and you are defining a function inside another function, which is an error in standard C.

nielsm
Jun 1, 2009



Super Ninja Fish posted:

I've got a C question about Linked Lists.

What does the "nested functions are disabled, use -fnested-functions to re-enable" error mean in C?
It means it thinks you tried to define a function inside another function.

Super Ninja Fish posted:

code:
    struct node *result = NULL;
    struct node result->first = NULL; //This is where the error happens.

I'm not sure how GCC actually decides you are trying to define a nested function here, but fact is that you're completely messing up variable declarations, initialisation and struct member access. (Nothing to do with linked lists per se.)

First line there declares result as a pointer to a node struct, and initialises it to NULL.
Then the second line declares a node... wait what no it tries to do member access in result what no, you can't do member access while declaring something, that doesn't make sense. Besides, you just initialised result to NULL so trying to dereference it and access any members would result in an access violation error at runtime.

Tell me, when result points to nothing, when you assign something to result->first where did you intend it to be stored?

I think the compiler might have thought you were defining a function because you named a type and then went on to do something more complex than declaring a variable of it.

some bust on that guy
Jan 21, 2006

This avatar was paid for by the Silent Majority.
That makes sense, result shouldn't start as null, but I'm not sure what it should point to because I want it to be an empty list that didn't have any elements added yet.

code:
struct node result = malloc(sizeof(struct node));
struct node result->first = NULL;
set7.c: In function ‘copy’:
set7.c:273: error: invalid initializer
set7.c:274: error: nested functions are disabled, use -fnested-functions to re-enable
set7.c:274: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
set7.c:274: error: expected expression before ‘->’ token

To get the main link list started, all I had to do was type

struct node *first = (struct node *) NULL;
struct node *last = (struct node *) NULL;
int size = 0;

at the top of the program, right after I defined the node structure. I was just trying to duplicate that in copy.

What's the right way to make a copy then?

Edit: Nevermind, seems to work now with

code:
 struct node *result = (struct node *) sizeof(struct node);
result->first = NULL;
Thanks

some bust on that guy fucked around with this message at 03:53 on Nov 20, 2011

Nalin
Sep 29, 2007

Hair Elf

Super Ninja Fish posted:

code:
struct node result = malloc(sizeof(struct node));
struct node result->first = NULL;
code:
 struct node *result = (struct node *) sizeof(struct node);
result->first = NULL;
You are making a lot of errors here. It may be best for you to throw up your entire file onto a pastebin so we can see what you are trying to do.

nielsm
Jun 1, 2009



Okay, I'm guessing at what you mean by the fields in your node structure here.

element would be the data value.
next would point to the next element in the list, or NULL if this is the last element.
first and last would point to the first and last element in the list. This is bad.

If you have every element in a linked list point to the first and last element in the list, you lose most of the advantages of using linked lists. One of the major advantages of linked lists is that you can insert new elements at either end or even in the middle without having to touch every element of the list. If you suddenly make every element carry "data structure global" data such as pointers to the start and end of the list, you suddenly need to update every element of the list whenever you add or remove elements from the start or end of the list. This is very bad.

Because they add lots of unneeded (and undesirable) complexity, I'll remove the first and last fields of the node struct, simplifying it to:
code:
struct ListNode {
  struct ListNode *next;
  int data;
};
Now, how do you declare a list? You just need a pointer to the first element of the list to do that. Like this:
code:
struct ListNode *my_list;
That's it, a list declared. Notice that it isn't initialised to anything yet. Let's instead initialise it to the empty list: Simply a NULL pointer.
code:
struct ListNode *my_list = NULL;
Okay, that's fine, not very useful yet.

How would a single element list look? It would have one ListNode allocated somewhere, and a struct ListNode * variable pointing to that memory. The ListNode would have a next pointer of NULL, because it's the last element in the list, and it would have its data member set to something.

So how do you create such a list? You need to allocate the appropriate amount of memory and set up the pointers.
code:
struct ListNode * create_node(int data) {
  /* declare a variable for the result */
  struct ListNode *result;
  /* allocate the amount of memory required for one struct ListNode and point to that new allocation */
  result = (struct ListNode *)malloc(sizeof(struct ListNode));
  /* initialise the fields of the new struct */
  result->next = NULL;
  result->data = data;
  /* done! */
  return result;
};
You could use that function like this:
code:
int main() {
  struct ListNode *my_list = create_node(42);
  return 0;
}
Not very interesting yet... but with this we can also create a list with two elements in it! We just need to set the next pointer of one of the nodes to point to the other one.
code:
int main() {
  struct ListNode *node1, node2;
  node1 = create_node(1);
  node2 = create_node(2);
  node1->next = node2;
  return 0;
}
This makes node1 point to the first element in a list with two elements. node2 doesn't "know" about this, actually, but it doesn't matter much.

Now that we can create lists, let's do something. How about counting the number of elements?
code:
int list_length(struct ListNode *list) {
  int result = 0;
  struct ListNode *current = list;
  /* loop as long as the remaining part of the list is not empty */
  while (current != NULL) {
    /* we had a non-NULL element so add one */
    result++;
    /* now have current point to the following element */
    current = current->next;
    /* if the next element was NULL i.e. end of list, the loop will terminate */
  }
  return result;
}
That function shows the basic way of iterating through a linked list. With iteration, it shouldn't be so hard to write a function to clone an entire list!
code:
struct ListNode * list_clone(struct ListNode *list) {
  struct ListNode *current_source, *current_destination, *result;

  /* do a quick check that we aren't cloning an empty list */
  if (list == NULL)
    return NULL;

  /* begin with the first element */
  current_source = list;
  result = create_node(list->data);
  current_destination = result;

  /* this is a bit different from last time, see if you can figure out why */
  while (current_source->next != NULL) {
    /* move to next source node */
    current_source = current_source->next;
    /* add a new node to the end of the destination, copying the data */
    current_destination->next = create_node(current_source->data);
    /* and move to the new end of the destination list */
    current_destination = current_destination->next;
  }

  /* quiz: why did we need both a result and a current_destination anyway? */
  return result;
}
As for why this is wrong:
code:
struct node foo->next = NULL;
The form of declaring a variable is this:
type_of_variable variable_name [= initial_value];
The problem is that foo->next is not a variable name, it's an expression! It's an expression that says, "take the pointer at foo, dereference it, then access the field named next in the structure that was pointed to."
You can't (and wouldn't need to) declare fields of a structure like that, you just access them.
Consider this statement:
code:
result->data = data;
It's an assignment. There are two parts to an assignment: The left hand side and the right hand side. The left hand side must be something that can be assigned to, an lvalue in compiler lingo. The right hand side must be a value that is compatible with the type of the lvalue.
In this case, our lvalue is result->data, the field data in the structure pointed to by the result variable. The rvalue is simply the local variable named data. Note that the data field and the local variable data are not related at all, their names don't clash because the field's name lives inside the structure type declared.


I hope this has been helpful.


Edit:

quote:

code:
 struct node *result = (struct node *) sizeof(struct node);
result->first = NULL;
Nonononono!
What you are doing here: You declare a pointer variable. Then you calculate the size of the node structure. Then you tell the compiler that this size of the structure is in fact a valid pointer to a node structure, which I can tell you it most likely is not. You then proceed to write to the memory pointed to by that bogus pointer, which will cause a crash by access violation (segmentation fault).

nielsm fucked around with this message at 04:14 on Nov 20, 2011

some bust on that guy
Jan 21, 2006

This avatar was paid for by the Silent Majority.

nielsm posted:

I hope this has been helpful.

Yeah, that helped out incredibly, thanks a lot for taking the time. I would have never figured that out on my own. I would have probably went on trying to do linked lists nodes with first and last forever.

quote:

code:
struct ListNode * list_clone(struct ListNode *list) {
  struct ListNode *current_source, *current_destination, *result;

  /* do a quick check that we aren't cloning an empty list */
  if (list == NULL)
    return NULL;

  /* begin with the first element */
  current_source = list;
  result = create_node(list->data);
  current_destination = result;

  /* this is a bit different from last time, see if you can figure out why */
  while (current_source->next != NULL) {
    /* move to next source node */
    current_source = current_source->next;
    /* add a new node to the end of the destination, copying the data */
    current_destination->next = create_node(current_source->data);
    /* and move to the new end of the destination list */
    current_destination = current_destination->next;
  }

  /* quiz: why did we need both a result and a current_destination anyway? */
  return result;

After 30 minutes of looking at this part and trying it out with just using result, I see that current_destination at the only refers to the last element while result still refers to the first.

So I can tell if I have this straight,

current_destination->next = create_node(current_source->data);

The above changes both result and current_destination, due to "next" being a pointer, while the below changes only current_destination.

current_destination = current_destination->next;

Wow, I'm glad I came here and asked. I thought I knew what I was doing when it came to linked lists, but I didn't.

nielsm
Jun 1, 2009



Super Ninja Fish posted:

Yeah, that helped out incredibly, thanks a lot for taking the time. I would have never figured that out on my own. I would have probably went on trying to do linked lists nodes with first and last forever.

It would, however, make sense to have a "master" structure that represents the list as a whole, and that structure then refers to the first and last elements, and whenever you want to manipulate the list, you need to reference that "master" as well as possibly a node to work on. That allows you to access both ends of the list directly as well as e.g. keeping a count of elements. If you don't keep explicit end pointers and counts you have to iterate through the entire list every time you need to either know the count or do something on the other end of it.

Super Ninja Fish posted:

After 30 minutes of looking at this part and trying it out with just using result, I see that current_destination at the only refers to the last element while result still refers to the first.
Yes, because we need to work on the back end of the list but return the front end, and it's only possible to walk in one direction, from front to back, with this type of list.

This type of list is also called a singly linked list, because each node only links in one direction. You can also create doubly linked lists, where each node links to both its successor and its predecessor. It uses a bit more memory per node (because you need two pointers instead of just one), but it can be walked in both directions.
On a side note, the std::list data structure in C++ is a doubly linked list with a "master".


Continuing with the singly linked structure above, what happens here and why is it bad?
code:
struct ListNode * prepend_something(struct ListNode *list) {
  struct ListNode *new_node = create_node(10);
  new_node->next = list;
  return new_node;
}

int main() {
  struct ListNode *list = ...; /* let's pretend a long list of several elements is made here */
  struct ListNode *list2 = prepend_something(list);
  struct ListNode *list3 = prepend_something(list);
  print_list(list);  /* imaginary function that would show the contents of a list */
  print_list(list2);
  print_list(list3);
  return 0;
}
Do you have any suggestions for fixing it?

Walked
Apr 14, 2003

Gonna re-ask this:

Good C++ primer/book/material for a guy who knows OOP/programming basics of higher level languages (C#, python) but wants to elarn C++?

nielsm
Jun 1, 2009



Walked posted:

Gonna re-ask this:

Good C++ primer/book/material for a guy who knows OOP/programming basics of higher level languages (C#, python) but wants to elarn C++?

Try Stroustrup's The C++ Programming Language, it's written for programmers. You can read some excerpts from it at Stroustrup's website. (He writes a 4th edition which covers C++11 is in the works, but finished at earliest summer 2012. The current Special Edition is still good :))

Walked
Apr 14, 2003

nielsm posted:

Try Stroustrup's The C++ Programming Language, it's written for programmers. You can read some excerpts from it at Stroustrup's website. (He writes a 4th edition which covers C++11 is in the works, but finished at earliest summer 2012. The current Special Edition is still good :))

Thank you. The excerpts are awesome. Going to pick this up.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I am having another battle with Visual Studio 2010 over porting a project. In this case, there is a unit_tests.cpp file sprinkled throughout a project in different subdirectories, testing stuff relevant to that subdirectory. I am getting a lot of this when trying to link:

code:
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>main.obj : error LNK2005: _main already defined in controller_example.obj
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
1>Debug\unit_tests.obj : warning LNK4042: object specified more than once; extras ignored
I was on the fence before, but I think this is tipping me over the edge of giving each of those files a unique name. Still, I want to know if there's a way around this in case I see a need to have a similar name.cpp file floating around in a project some day.

Edit: Oh well I guess I see what I could do: http://stackoverflow.com/questions/3695174/visual-studio-2010s-strange-warning-lnk4042

Rocko Bonaparte fucked around with this message at 07:19 on Nov 21, 2011

Paolomania
Apr 26, 2006

Rocko Bonaparte posted:

Edit: Oh well I guess I see what I could do: http://stackoverflow.com/questions/3695174/visual-studio-2010s-strange-warning-lnk4042

If you are attached to using VS, another option is to split the project into multiple projects all under one top-level "solution". VS flattening a project's build space into one object directory makes some sense since everything that gets statically linked together ultimately can't have conflicting symbols anyways, and symbols tables do not care at all about the structure of your source tree.

TasteMyHouse
Dec 21, 2006

nielsm posted:


code:
int main() {
  struct ListNode *node1, node2; // Ooops!
  node1 = create_node(1);
  node2 = create_node(2);
  node1->next = node2;
  return 0;
}

Yo, nielsm, your post was great in general, but "struct ListNode *node1, node2;" declares node1 as a pointer-to-ListNode and node2 as a ListNode. See:
http://codepad.org/cPWA3Jca

nielsm
Jun 1, 2009



TasteMyHouse posted:

Yo, nielsm, your post was great in general, but "struct ListNode *node1, node2;" declares node1 as a pointer-to-ListNode and node2 as a ListNode. See:
http://codepad.org/cPWA3Jca

Turns out I didn't proof-read everything!
If I was writing that for my own use I would have made a typedef struct ListNode *PListNode; or similar, maybe I should have done that anyway.

Mr.Radar
Nov 5, 2005

You guys aren't going to believe this, but that guy is our games teacher.

nielsm posted:

Turns out I didn't proof-read everything!
If I was writing that for my own use I would have made a typedef struct ListNode *PListNode; or similar, maybe I should have done that anyway.

I generally don't like typedefs for pointer types because they become misleading when combined with const. For example:

code:
typedef struct Foo_tag { int bar; } Foo, *pFoo;

void func(const pFoo y) {
    y->bar++;
}

int main(int argc, char *argv[]) {
    Foo x = {0};
    func(&x);
    printf("%d\n", x.bar);  // Prints 1
    return 0;
}
This is perfectly legal code because the const qualifier on the argument to func applies to the pointer type and not to the pointed type, even though I meant the const to apply to the pointed type. This isn't theoretical either; I've seen this bug in production code written by experienced C developers (though thankfully never where there was an actual bug the const would've caught if it were applied correctly).

Mr.Radar fucked around with this message at 04:17 on Nov 22, 2011

InAndOutBrennan
Dec 11, 2008
I'm tooling around with C++, mostly trying to learn stuff.

Suppose I would want to test if I'm out of bounds of my allocated array, is this ok?

code:
{
  char *test = new char[10];
  char *end;
  end = test + 10;
  
  char *curpos;
}
End would point to an address outside of the allocated memory and I could check this by:

code:
{
  if(curpos == end)
  {
  //sort it out
  }
}
Is there any case where actually trying to set or access the address of end would be illegal?

edit: I could of course check for the last position in the allocated array instead but I'm curious.

nielsm
Jun 1, 2009



You can calculate and compare pointers all you want, things can only go wrong when you go about dereferencing them. After all, pointers are just numbers the compiler assigns a special meaning.

Trying to access one element past the end of an array may not trigger a runtime error, but it is an error nonetheless. You may get lucky and hit a page not allocated by the OS, which gives you an access violation, but you may just as well hit some memory the runtime library's allocator has obtained from the OS but not allocated to you yet, or you may hit data from a different allocation.

But having a pointer one past the end of the allocation is basically what the STL container types do with their iterators, giving the idiom: for (iterator bar = foo.begin(); bar != foo.end(); ++bar)

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
You're allowed to offset up to one element past the array, and you're still guaranteed to get a valid pointer. So your proposed usage of having a pointer one past the end so that you know when to stop works fine.

Actually dereferencing the pointer, on the other hand, is not guaranteed to work. In fact, the standard explicitly calls out that there might only be a single byte at that point, and that byte might be used by some other object - all that's required is that the pointer itself be "valid".

InAndOutBrennan
Dec 11, 2008
Allright, thanks!

shrughes
Oct 11, 2008

(call/cc call/cc)

nielsm posted:

Trying to access one element past the end of an array may not trigger a runtime error, but it is an error nonetheless. You may get lucky and hit a page not allocated by the OS, which gives you an access violation, but you may just as well hit some memory the runtime library's allocator has obtained from the OS but not allocated to you yet, or you may hit data from a different allocation.

Or worse, you might write to the allocator's internal data structures.

raminasi
Jan 25, 2005

a last drink with no ice

Jabor posted:

You're allowed to offset up to one element past the array, and you're still guaranteed to get a valid pointer. So your proposed usage of having a pointer one past the end so that you know when to stop works fine.

Actually dereferencing the pointer, on the other hand, is not guaranteed to work. In fact, the standard explicitly calls out that there might only be a single byte at that point, and that byte might be used by some other object - all that's required is that the pointer itself be "valid".

What does it mean for the pointer to be "valid" if it's not dereferencable?

Adbot
ADBOT LOVES YOU

shrughes
Oct 11, 2008

(call/cc call/cc)

GrumpyDoctor posted:

What does it mean for the pointer to be "valid" if it's not dereferencable?

It means the pointer's okay but you can't dereference it.

Basically,

code:
int *x = new int[16];
int *y = x + 16;  // y is valid, and x < y, you can't do *y though.
int *z = x + 17;  // undefined behavior or something
Certainly there is no guarantee that x < z, that z-1 == y, or that anything useful can be said about the value of z.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply