C++ sort array of char pointers - c++

Can you tell me what's wrong with my method? I ends up putting the same thing everywhre and it's actually not sorting.
void sortArrays(){
int i, j;
for(i=0; i<counter; i++){
for( j=0; j<i; j++){
if( strcmp(title_arr[i], title_arr[j]) < 0){
char* title_temp = title_arr[i];
title_arr[j] = title_temp;
}
}
}

This:
char* title_temp = title_arr[i];
title_arr[j] = title_temp;
Is equivalent to:
title_arr[j] = title_arr[i];
You never swap them, you just copy one to the other. You should add this line:
title_arr[i] = title_arr[j];
In between the two. That way, you'll overwrite [i] with [j], but _temp still holds the old value of [i], so you can copy that value into [j], thus swapping them.
I suppose it's also a time for a lesson on algorithms. Your algorithm is known as a "bubble sort" algorithm. It is known for it's simplicity, but in a realistic setting it is known for it's inefficiency (the technical term is "teh sux", and the real technical term is O(n^2) ("N squared") performance). Some more common (and more efficient) algorithms include Quicksort, merge sort, and Heapsort, among many others. For more about measuring algorithmic scalability, see the article on Big Oh notation.*
But, as vava pointed out in a comment, unless your assignment is to write your own sorting function, you're going to get better performance with qsort (in C) or std::sort (in C++).
int mystrsort(const void *a, const void *b)
{
return strcmp(*(const char **)a, *(const char **)b);
}
// later:
qsort(title_arr, sizeof title_arr / sizeof(char *), sizeof(char *), mystrsort);
I'm not going to stab at std::sort, but it's going to work about the same (perhaps easier).**
*Note that anyone who likes is free to change these Wikipedia links to Stack Overflow links. It would be better to link to SO, I just linked to Wikipedia because I knew how to find the info I needed faster.
**Note that anyone who likes is free to add a std::sort example. I'm just not sufficiently familiar with C++.

You didn't swap properly, that's why it didn't work.
#include <iostream>
#include <algorithm>
int const counter = 4;
char * title_arr[counter] = {
"d", "c", "b", "a"
};
void sortArrays(){
for(int i = 0; i < counter; i++){
for(int j = 0; j < i; j++){
if(strcmp(title_arr[i], title_arr[j]) < 0){
char* title_temp = title_arr[i];
title_arr[i] = title_arr[j];
title_arr[j] = title_temp;
//you wouldn't have made that stupid mistake this way.
//std::swap(title_arr[i], title_arr[j]);
}
}
}
}
int compare(void const * a, void const * b) {
return strcmp(static_cast<char const *>(a), static_cast<char const *>(b));
}
struct StringLess : public std::binary_function<char const *, char const *, bool> {
bool operator() (char const * a, char const * b) const {
return strcmp(a, b) < 0;
}
};
int main(int argc, char * argv[])
{
sortArrays();
//those ones better
// qsort(title_arr, counter, sizeof(char *), compare);
// std::sort(title_arr, title_arr + counter, StringLess());
for (int i = 0; i < counter; i++) {
std::cout << title_arr[i] << ", ";
}
return 0;
}

Bad coding style:
1. Don't use global variables. It's better to pass your array and length as arguments into sort function. Why? Your function is not reusable. What if you will need to sort another array? Yes, you will need to write another sort function...
2. More advanced tip: use emulation of higher-order function. What if you will need to sort not only characters? Integer, floats, strings or your own types. In this case you can also pass compare() function into your sort function which can compare objects of your array.

Related

Converting char[][] to a char**?

I'm eradicating std::string in favor of C-strings, which I'm new to. How do I get the following to compile? g++ complains: cannot convert char(*)[16] to char**
#include <iostream>
void print(char** s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << '\n';
}
}
int main()
{
constexpr int n = 3;
char s[n][16]{ "Hello", "Bye", "Sky"};
print(s, n);
}
You created a multidimensional array, not an array of pointers. Usually an array can be said to be equivalent to a pointer, however in this case c++ needs to know the size of the second dimension of your array. The function would be as follows
void print(char s[][16], int n)`{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
Understandably you may want to pass the function using pointers as to not make an entire copy of the 2-d array. I saw you mentioned you were okay with variable length strings. That functionality is supported in the string library. You are dealing with c-strings which are not strings at all but static arrays of type character. Defining these c-strings using dynamic memory happens to give you the desired behavior as you create in the simplest terms an array of pointers.
void print(char** s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
int main()
{
int n = 3, i;
char** s = new char*[n];
for (i = 0; i < 3; i++) {
s[i] = new char[16];
}
s[0] = "Hello";
s[1] = "Bye";
s[2] = "Sky";
print(s, n);
for (i = 0; i < 3; i++) {
delete [] s[i];
}
delete [] s;
s = NULL;
return 0;
}
Since you are using dynamic memory now you need to free your memory which is what the last loop serves to do. As you can see using all this dynamic memory is quite taxing and it would be easier to use the string library that has been optimized to do a much better job then you can. If you're still not convinced you should at least make your own string class to handle the dynamic memory that contains a char * as its private member. In either case you avoid this mess and just make an array of zed class objects and not deal at all with multidimensional nonsense. No one likes seg faults and memory leaks.
Given any type T, T arr[N]; declares a variable arr of type T[N], which is an array and not a pointer. When you use arr in almost all contexts, array to pointer conversions happen, giving the incorrect illusion that arr is a pointer of type T*.
char s[n][16] = { "Hello", "Bye", "Sky" };
declares s as an array of n elements of type char[16]. Now, when array to pointer conversion happens, s decays into a pointer of type char (*)[16]. Hence, your function needs to have the signature
void print(char (*s)[16], int n);
Which is equivalent to
void print(char s[][16], int n);
the [] is interpreted as a pointer by the compiler.
To make these complex types more readable, a type alias may be used.
using T = char[16];
void print(T s[], int n);
Addressing some concerns
As pointed out in the comments, std::string should almost always be preferred over a char array. If you have performance concerns, benchmark before doing this. I really doubt much performance gains can be observed in most cases.
Declaring an array with length n which is an int is not standard C++. It is an extension provided by your compiler, it is not portable and in most cases not necessary.
int n = 3;
char vla[n]; // this is a variable length array
char arr[3]; // this is just an array
char* darr = new char[3]; // this is a pointer pointing to dynamically allocated memory
std::string str; // but instead, this is just better
The compiler cannot extract from char ** the infomation about char[16]. You need to define a type char[16] and pass the pointer to this type to your print function.
#include <iostream>
typedef char str_t[16];
void print(str_t* s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
int main()
{
int n = 3;
char s[n][16]{ "Hello", "Bye", "Sky"};
print(s, 3);
}

C++ function to find maximum element of 3 arrays

I'm currently learning arrays and how they work. I'm given the following function that is used to find the maximum elements in three different arrays, A, B and C:
void findMax(int A[], int B[], int C[])
{
int maxA = A[0], maxB = B[0], maxC = C[0];
for (int i = 1; i < MAX_LEN; i++)
{
if(maxA < A[i]) maxA = A[i];
if(maxB < B[i]) maxB = B[i];
if(maxC < C[i]) maxC = C[i];
}
}
My goal is to figure out how to return all three values (maxA, maxB, maxC) without adding extra arguments. I'm allowed to change the return type and I can write code outside the function.
My first thought was to change void into int and create a new array that has those three max values. However, I think I need pointers to return arrays and we haven't learned that yet.
There are a few ways to approach this.
The Traditional Approach (Arrays)
The easiest way is to make the function return an int[]. This gives back all of the values easily and in a very concise format. However, the int[] type cannot guarantee that its size is 3 or that it contains what you claim it contains, so this is not a very type-friendly approach.
The OO Approach (Structs)
Most JAVA enthusiasts will tell you to make a struct or a class that encapsulates the idea, such as this.
struct MaxVals {
int maxA;
int maxB;
int maxC;
// Possibly a constructor here
};
This is still a very memory-efficient solution and is much cleaner than the array approach. It also has the benefit of being more type-safe; you can't make a MaxVals with four or two ints now; it's guaranteed at compile-time to have 3. However, this is bulky. It requires you to define a new type, completely outside of your function, that will only ever be used in this one case.
The Modern Approach (Tuples)
This is a new feature of C++, adapted from Python and Haskell, so it's only available in C++11, which has limited support across compilers. Tuples are a new data structure in the tuple header which can guarantee heterogeneous, fixed-size data structures of any types you specify at compile-time. You would set your return type to std::tuple<int, int, int> and initialize the structure with std::make_tuple(maxA, maxB, maxC).
This approach has the same memory advantages and guarantees as the struct approach but without all the overhead and boilerplate of declaring a one-use type. This would be the ideal approach for a problem like this, if your compiler supports it.
There are two things, you can do:
The first is declare maxA, maxB, maxC out side the function (globally). Then access them in your findMax function and main function.
int maxA, maxB, maxC;
void findMax(int A[], int B[], int C[])
{
maxA = A[0], maxB = B[0], maxC = C[0];
for (int i = 1; i < MAX_LEN; i++)
{
if(maxA < A[i]) maxA = A[i];
if(maxB < B[i]) maxB = B[i];
if(maxC < C[i]) maxC = C[i];
}
}
The second is use structure.
struct node
{
int maxA,maxB,maxC;
};
struct node findMax(int A[], int B[], int C[])
{
struct node Max_node;
Max_node.maxA = A[0], Max_node.maxB = B[0], Max_node.maxC = C[0];
for (int i = 1; i < MAX_LEN; i++)
{
if(Max_node.maxA < A[i]) Max_node.maxA = A[i];
if(Max_node.maxB < B[i]) Max_node.maxB = B[i];
if(Max_node.maxC < C[i]) Max_node.maxC = C[i];
}
return Max_node;
}
P.S: you also could use pointers, but as you said you are unfamiliar with them, I used other ways.

Use template or void?

I need to implement a small function which does the byte swapping for an array with given length.
void myFunc (MYTYPE arrayPointer, const int& length)
{
unsigned int* tmp = (unsigned int*) arrayPointer;
for (int j=0; j < length; j = j + 4)
{
*tmp = htonl (*(tmp));
tmp++;
}
}
I don't want the user to do typecasting while using the function. I think I have two options left:
1. Replace MYTYPE with (void*)
2. Use templates.
Which option is better or preferable? Is there any better way than these two?
Templates, obviously. You're going to need an enable_if on sizeof(T)==4.

multimap on C-style string key fails to insert entries

I am trying to create a multimap indexed on a C-style string, as shown in the following code segment:
#include <cstring>
#include <map>
#include <iostream>
using namespace std;
int main(void)
{
int i, j;
int (*fn_pt)(const char *, const char *) = strcmp;
multimap<char *, char *, int (*)(const char *, const char *)>a(fn_pt);
for (i = 0; i < 2; i++)
{
char key[2];
sprintf(key, "%d", i);
for (j = 0; j< 5; j++)
{
char value[2];
sprintf(value, "%d", j);
a.insert(pair<char *, char *>(key, value));
}
}
for (i = 0; i < 2; i++)
{
char key[2];
sprintf(key, "%d", i);
multimap<char *, char *>::iterator it = a.find(key);
while (it != a.end())
{
cout << it->first << "\t" << it->second <<endl;
it++;
}
}
}
Simply changing the key in the above program to integer gives me the expected result. But, indexing the multimap on a string is giving me something unexpected (only rows of 1's and 4's separated by space), instead of showing me every value for every key value used.
Where am I going wrong in this?
Thanks
strcmp is a wrong predicate to use in multimap.
The predicate shall satisfy the following:
The expression comp(a,b), where comp is an object of this comparison class and a and b are key values, shall return true if a is to be placed at an earlier position than b in a strict weak ordering operation.
strcmp violates that because it returns a nonzero value if the strings are unequal, either a < b or a > b.
You should define your own predicate which returns true if and only if the first string is less than the second.
multimap<char *, char *, int (*)(const char *, const char *)>a(fn_pt);
for (i = 0; i < 2; i++)
{
char key[2];
sprintf(key, "%d", i);
for (j = 0; j< 5; j++)
{
char value[2];
sprintf(value, "%d", j);
a.insert(pair<char *, char *>(key, value));
}
}
You're storing two pointers in a container, and then you're destroying the objects (key and value) those pointers point to when they go out of scope. This leaves the container holding information that is now meaningless.
You're using the memory of key and value long after they go out of scope. In fact, all your char* pointers point at the same piece of stack memory, and that piece is ready to be reused by the time you actually look at it.
To do what you want you need to use strdup() to create a permanent copy of your char * data. Of course, then you need to worry about deallocating it later.

c++ max choosing sort

I have started to learn c++. (I learn applied maths at school) And I must learn programming. I have just wrote a max choosing sort in c++. It should print the elements of a predefined array in decreasing order, but it is not working. Can you tell me where did I go wrong?
int* max(int *array)
{
int *max = array++;
while(*array)
{
if (*max<*array) *max = *array;
array++;
}
return max;
}
void change (int *what, int *to)
{
int *temp = what;
what =to;
to = temp;
}
void sort(int *array)
{
while(*array) change(array,max(array));
array++;
}
int _tmain(int argc, _TCHAR* argv[])
{
int A[] ={7,5,6,9,2,5,3,1,4,10,6,7,2,8};
sort (A);
int i =0;
while (A[i]) cout<<A[i++];
getchar();
return 0;
}
The first problem I see is that while (*array) { ... } won't terminate where you want it to because what is beyond the last element of your array is not certain to be 0 or NULL, or it may terminate in the middle of your array if you are sorting an array that includes 0.
Instead you want to say something like:
int A[] = { ... };
int element_count = sizeof(a)/sizeof(a[0]);
for (int i = 0; i < element_count; ++i) { ... }
You seem to have the concepts of pointers and values reversed. For example, your change function, as it is now, is
void change (int *what, int *to)
{
int *temp = what;
what =to;
to = temp;
}
when it should be
void change (int *what, int *to)
{
int temp = *what;
*what = *to;
*to = temp;
}
Which is almost exactly the opposite.
There are also several misuses of pointers in the max function that I can see as well as the sort function, and that is what I see after only a very brief glance at the code. You need to go back and learn pointers, and then try this again.
The second problem is that the array++; in the sort() function is not part of the while loop. I wouldn't be suprised if this never exits.
I would try to explicitly show what you're trying to do by using the right type; for example:
while (*array)
{
...
}
would be a lot easier for you to debug if it said something more like
while (array != NULL)
or even
for (int k=0; k<n; k++)
{
// do something with array[k]
}
Also, your change function seems like it should swap two values that the pointers point to, but it doesn't.
Just in case it helps, I think the technical name of what you're doing is called "Selection Sort". This might help if you need to ask anyone else for advice!
Good luck!